Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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,

like image 878
Loic Duros Avatar asked Feb 09 '14 10:02

Loic Duros


People also ask

Does the order of inheritance matter in multiple inheritance?

Yes, you can do multiple inheritance. please note the order of class in ExampleSimMod matters.

Does order matter in multiple inheritance in Python?

When we search for an attribute in a class that is involved in python multiple inheritance, an order is followed. First, it is searched in the current class. If not found, the search moves to parent classes. This is left-to-right, depth-first.

How does Python handle multiple inheritance?

A class can be derived from more than one base class in Python, similar to C++. This is called multiple inheritance. In multiple inheritance, the features of all the base classes are inherited into the derived class. The syntax for multiple inheritance is similar to single inheritance.

What is Python method resolution order?

The Python Method Resolution Order defines the class search path used by Python to search for the right method to use in classes having multi-inheritance. It as envolved since Python 2.2 to 2.3. The algorithm change is also refered as old classes and new classes MRO algorithm.

What is __ MRO __ in Python?

The Method Resolution Order (MRO) is the set of rules that construct the linearization. In the Python literature, the idiom "the MRO of C" is also used as a synonymous for the linearization of the class C.


1 Answers

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.

like image 52
Bakuriu Avatar answered Oct 06 '22 00:10

Bakuriu