[Python-ideas] Consider (one day) adding an inheritance order class precedence mechanism
Steven D'Aprano
steve at pearwood.info
Wed Nov 15 19:18:41 EST 2017
On Wed, Nov 15, 2017 at 01:49:03PM -0800, Neil Girdhar wrote:
> Sometimes I get MRO failures when defining classes. For example, if
>
> R < E, C
> B < S, E
> S < C
> Z < B, R
>
> Then Z cannot be instantiated because C precedes E in B and E precedes C in
> R. The problem is that when the inheritance graph was topologically-sorted
> in B, the order was S, C, E. It could just as easily have been S, E, C.
These are not equivalent:
B < S, E
B < E, S
so your comment that it could "just have easily" been S, E, C is not
generally true. Calling
C.method(self)
E.method(self)
in that order is not, in general, the same as calling them in the
opposite order.
> So, one solution is to add C explicitly to B's base class list, but this is
> annoying because B might not care about C (it's an implementation detail of
> S). It also means that if the hierarchy changes, a lot of these added
> extra base classes need to be fixed.
>
> I propose adding a magic member to classes:
>
> __precedes__ that is a list of classes. So, the fix for the above problem
> would be to define E as follows:
>
> class E:
> from whatever import C
> __precedes__ = [C]
As the author of E, why would I do that? I don't even know that B
exists, and even if I did know about B, by adding __precedes__ I risk
breaking classes X, Y and Z which expect C to precede E.
> Then, when topologically-sorting (so-called linearizing) the ancestor
> classes, Python can try to ensure that E precedes C when defining B.
How? You're glossing over the most important detail: *how* should Python
produce a consistant, monotonic, bug-free linearization of the
superclasses given an arbitrary number of (possibly conflicting)
__precedes__ constraints?
If __precedes__ is a hard constraint, then you have to deal with
inconsistent constraints *as well as* inconsistent inheritence orders.
If __precedes__ is just a suggestion, rather than a hard constraint,
then you have to deal with the cases where the actual MRO ignores the
suggestion, leading to contradiction between what the source says and
what the code actually does.
In either case, we know have an even more complicated set of inheritence
rules, with even more things that can go wrong. Getting the MRO for
multiple inheritence right is hard enough already. Python's current MRO
algorithm is the third, the first two were broken:
http://python-history.blogspot.com.au/2010/06/method-resolution-order.html
https://www.python.org/download/releases/2.3/mro/
Letting people mess with the MRO will surely lead to subtle and hard to
diagnose bugs.
The harsh truth is that sometimes you just do not have a consistent MRO
for a certain set of superclasses. Python refuses to let you use such an
inconsistent MRO, not because it is trying to be annoying, but because
such inconsistent MROs are broken.
If you know of an alternative MRO that gives correct results for the
class hierarchy you give above, please share.
--
Steve
More information about the Python-ideas
mailing list