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