Python uses something called C3 linearization to decide what order the base classes are in: the "method resolution order". This has basically two parts when stated informally:
The path must hierarchy must never go down from a class to its superclass, even indirectly. As such, no cycles are allowed and issubclass(X, Y) and issubclass(Y, Z) implies issubclass(X, Z).
The order when not forced by the rule above is ordered by number of steps to the super-most class (lower number of steps means earlier in the chain) and then the order of the classes in the class lists (earlier in the list means earlier in the chain).
The hierarchy here is:
A / \ / \ B1 B2 # Possibly switched \ / \ / object
In the first case the order after C3 linearization is
super super super A → B1 → B2 → object
which we can find out with:
A.mro() #>>> [<class 'A'>, <class 'B1'>, <class 'B2'>, <class 'object'>]
So the super() calls would resolve as:
class A(B1, B2): pass class B1: def f(self): # super() proxies the next link in the chain, # which is B2. It implicitly passes self along. B2.temp(self) class B2: def temp(self): print("B2")
so calling A().f() tries:
- Is
f on the instance? No, so - Is
f on the first class, A? No, so - Is
f on the next class, B1? Yes!
Then B1.f is called and this calls B2.temp(self), which checks:
- Is
f on the class, B2? Yes!
And it is called, printing B2
In the second case we have
super super super A → B2 → B1 → object
So the resolves
So the super() calls would resolve as:
class A(B2, B2): pass class B2: def temp(self): print("B2") class B1: def f(self): # super() proxies the next link in the chain, # which is B2. It implicitly passes self along. object.temp(self)
- Is
f on the instance? No, so - Is
f on the first class, A? No, so - Is
f on the next class, B2? No, so - Is
f on the next class, B1? Yes!
So B1.f is called and this calls object.temp(self), which checks:
- Is
f on the class, object? No, - There are no superclasses, so we have failed to find the attribute.
- Raise
AttributeError("{!r} object has no attribute {!r}".format(instance, attribute_name)).
B2doesn't implementf, so if it's looked up there first (in the second case) it can't be found.