[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