[Python-Dev] python and super

Nick Coghlan ncoghlan at gmail.com
Fri Apr 15 20:07:43 CEST 2011


On Fri, Apr 15, 2011 at 11:30 PM, Michael Foord
<fuzzyman at voidspace.org.uk> wrote:
> On 15/04/2011 02:02, Greg Ewing wrote:
>> There isn't necessarily a clear distinction between parents
>> and siblings.
>>
>> class A:
>>  ...
>>
>> class B(A):
>>  ...
>>
>> class C(A, B):
>>  ...
>>
>> In C, is A a parent of B or a sibling of B?

As has been pointed out elsewhere in the thread, that definition of C
isn't allowed :)

>>> class C(A, B): pass
...
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Cannot create a consistent method resolution
order (MRO) for bases A, B

Once you turn the order of definition around (class C(B, A)) it
becomes clear that A remains B's parent regardless of the existence of
C:

>>> C.__mro__
(<class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>,
<class 'object'>)

The whole discussion of trying to distinguish parents from siblings
when invoking super() *doesn't make sense*. The entire *point* of the
multiple inheritance handling is to linearise the type hierarchy into
a method resolution order that consists of a single chain of classes
that are called in sequence (with any class in the chain allowed to
terminate the sequence at any time).

Cooperative super() calls are exactly that: cooperative. Just as
cooperative threading breaks down if one task doesn't play by the
rules, such is also the case with cooperative super calls.

There are two ways to handle this:

- Option 1 is to tailor your inheritance hierarchy such that any
"non-cooperative" classes always appear on the right-most end of the
MRO (e.g. as "A" and "object" do in the example above). This can be
tricky, but is doable if there is just the one recalcitrant class
causing problems (e.g. I wouldn't be surprised to hear that a simple
rearrangement to "class MyTestCase(Mixin1, Mixin2, TestCase)"
sufficiently rearranged the "MyTestCase" MRO to make this problem go
away).

- Option 2 is to do as Raymond suggests: noncooperative classes are
incorporated via "has-a" composition (potentially as a proxy object)
rather than "is-a" inheritance. For any methods which require
cooperative calls, the cooperative wrapper provides that behaviour,
while delegating the heavy lifting to the underlying object.

Essentially, any cooperative hierarchy requires a base class that
defines the rules of cooperation and provides "no-op" termination
methods for any cooperative calls. Non-cooperative classes must either
be parents of that base class, or else they must be wrapped as
described in Option 2.

Cheers,
Nick.

-- 
Nick Coghlan   |   ncoghlan at gmail.com   |   Brisbane, Australia


More information about the Python-Dev mailing list