Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
970 views
in Technique[技术] by (71.8m points)

oop - Python and order of methods in multiple inheritance

In Python, if you define two classes with the same method and intend for those two classes to be parent classes, as:

class A(object):
     def hello(self):
         print "hello from class a" 

and:

class B(object):
     def hello(self):
         print "hello from class b"

when you define the child class and add the two parent classes in the order A and B:

class C(A, B):
     def __init__(self):
         self.hello()

the method that is used when calling self.method() is the one belonging to A, or the first class in the list of inheritance:

>>> C()
hello from class a
<__main__.C object at 0x10571e9d0>

While this seems to be true in all my test cases, I can't find a place in the docs or online that it is actually safe across any platform and implementation of the language. Can anyone either confirm it is safe to assume the first inherited class in the list will always be the method used over the others (regardless of super().__init__() calls etc) or point me towards an official documentation confirming this?

Thank you,

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

Yes it is guaranteed as described in the document that introduced the new algorithm to compute the method resolution order (which is C3 linearization).

Implementations that don't use this algorithm for their mro don't really conform to the python language (version 2.3+). AFAIK all current implementation do use C3 linearization.


C3 linearization satisfies the local precedence ordering and monotonicity properties. local precedence ordering means that a class C(B1, ..., Bn) will have in its mro the base-classes Bi in the order they were listed in the inheritance list.

Monotonicity is probably better explained with an example:

>>> class A(object): pass
>>> class B(object): pass
>>> class C(object): pass
>>> class D(object): pass
>>> class E(object): pass
>>> class K1(A,B,C): pass
>>> class K2(D,B,E): pass
>>> class K3(D,A):   pass
>>> class Z(K1,K2,K3): pass

The old mro for python2.2 (which is not monotonic), these are the linearizations of the above classes:

L[A] = A O
L[B] = B O
L[C] = C O
L[D] = D O
L[E] = E O
L[K1]= K1 A B C O
L[K2]= K2 D B E O
L[K3]= K3 D A O
L[Z] = Z K1 K3 A K2 D B C E O
# in current versions of python (2.3+):
# L[Z] = Z K1 K2 K3 D A B C E O

Here you can see that, in the linearization of Z, the class A comes before D, while in the linearization of K3 it comes after D. Monotonicity is the property of a linearization such that there are no swaps of this kind when inheriting. If a class X precedes class Y in all linearization of the parents of a class, then it will also precedes class Y in the final linearization.

Now, if we consider a class C(B1, ..., Bn). By local precedence order the classes B1, ..., Bn will be found in that order in the linearization of C. By monotonicity we cannot find subclasses of Bis before the Bi itself. From this follows that the linearization of C, if it exists, must start with C and B1.

Note that in some cases you can't compute the linearization, and python will complain, for example:

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

However if you swap the classes it is possible to linearize the hierarchy:

>>> class B(A, object): pass
... 
>>> B.mro()
[<class '__main__.B'>, <class '__main__.A'>, <class 'object'>]

If parent classes have no common base (other then object obviously), then it's clear that the linearization of C(B1, ..., Bn) will start with the linearization of B1 (except for object), then will follow the linearization of B2 etc and it will end with the linearization of Bn:

>>> class A(object): pass
... 
>>> class B(object): pass
... 
>>> class A1(A): pass
... 
>>> class A2(A1): pass
... 
>>> class B1(B): pass
... 
>>> class C(object): pass
... 
>>> class C1(C): pass
... 
>>> class C2(C1):pass
... 
>>> class C3(C2): pass
... 
>>> class D(A2, B1, C3): pass
... 
>>> D.mro()
[<class '__main__.D'>, <class '__main__.A2'>, <class '__main__.A1'>, <class '__main__.A'>, <class '__main__.B1'>, <class '__main__.B'>, <class '__main__.C3'>, <class '__main__.C2'>, <class '__main__.C1'>, <class '__main__.C'>, <class 'object'>]

Things start to get weird when you have some common subclasses between the Bis in which case either python finds the order that you'd expect that doesn't violate local precedence order and monotonicity or it will raise an error.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...