How does `super` interact with the attribute of the __mro__` class with multiple inheritance?

Today I read the white paper super .
In which he mentioned multiple inheritance, will be determined by the __mro__ attribute for the class.
Therefore, I experimented a bit, but its result surprised me.

 # CODE PART class GrandFather(object): def p(self): print "I'm old." class Father(GrandFather): def p(self): print "I'm male." class Mother(object): def p(self): print "I'm female." class Son(Father, Mother): def p(self): print "busy, busy, crwaling. " # EXPERIMENT PART In [1]: Son.__mro__ Out[1]: (__main__.Son, __main__.Father, __main__.GrandFather, __main__.Mother, object) In [2]: Father.__mro__ Out[2]: (__main__.Father, __main__.GrandFather, object) In [3]: Mother.__mro__ Out[3]: (__main__.Mother, object) In [4]: GrandFather.__mro__ Out[4]: (__main__.GrandFather, object) In [5]: s = Son() In [6]: super(Son, s).p() I'm male. In [7]: super(Father, s).p() I'm old. In [8]: super(Mother, s).p() --------------------------------------------------------------------------- AttributeError Traceback (most recent call last) <ipython-input-8-ce4d0d6ef62d> in <module>() ----> 1 super(Mother, s).p() AttributeError: 'super' object has no attribute 'p' In [9]: super(GrandFather, s).p() I'm female. 

The following is part of the official document mentioned above, it says:

 super(type[, object-or-type]) Return a proxy object that delegates method calls to a parent or sibling class of type. This is useful for accessing inherited methods that have been overridden in a class. The search order is same as that used by getattr() except that the type itself is skipped. The __mro__ attribute of the type lists the method resolution search order used by both getattr() and super(). The attribute is dynamic and can change whenever the inheritance hierarchy is updated. If the second argument is an object, isinstance(obj, type) must be true. 

By combining this document and the result of my experiment. The most confusing part is that when called with super(GrandFather, s).p() it calls p() from Mother , but Mother not in GrandFather __mro__ , and it is in very low order Son __mro__ .

After a little reflection. I received a plausible explanation that indicates the incompleteness or lack of an official document:
That is, when used with super(type, instance) , the super function searches from the __mro__ class attribute from which your instance is created, but not the __mro__ type attribute that you passed to super , even if it satisfies the isinstance(instance, type) condition.

So what happened when you typed super(Class, instance) :

  • Python checks to see if isinstance(instance, Class) is True.
  • Python will find the __class__ instance attribute,
    get the instance.__class__ __mro__ .
  • Python will find the class index you passed to super in the __mro__ tuple in step 2.
  • Python adds the index of step 3 to 1, uses it to get the corresponding class in the __mro__ tuple in step 2, and returns the super-delegate of this corresponding class.
  • If the index in step 4 exceeds the length of __mro__ for step 2, the delegate of the last class is __mro__ to __mro__ for step 2, which is the class object .

As far as I understand?
If I'm wrong, then what is the mechanism that super interacts with type __mro__ ?
If I am right, how should I raise the question of modifying a python whitepaper?
Because I think that the current version about this element can be misleading.


PS: This test was performed by Python 2.7.6 within IPython 3.2.1 .

+3
python inheritance super superclass
source share
2 answers

Check out __mro__ Son :

 __main__.Son, __main__.Father, __main__.GrandFather, __main__.Mother, object 

According to the document:

The __mro__ attribute of type enumerates the search order of the method solution.

Thus, the methods will be executed in order from the __mro__ list __mro__ left to right. A call to super(type, instance) will change the starting position to the type specified as the first argument to super() in the __mro__ list of the __mro__ class specified as the second argument (if the second argument passed to super is an instance):

super(Son, s) will be the __main__.Father proxy

super(Father, s) will be a proxy to __main__.GrandFather

super(GrandFather, s) will be a proxy to __main__.Mother

super(Mother, s) will be a proxy to object

The interesting part is why __mro__ of Son as it is. In other words, why Mother after Grandfather. This is due to how linearization works in python:

linearization C is the sum of C plus the merger of the linearizations of the parents and the list of parents.

See the examples in the documentation you mentioned, this explains a very similar case.

So, the end result is really correct: super(GrandFather, s).p() should be I'm female.

+2
source share

In chapter 32 of python training :

each super call selects a method from the next class following it in the MRO ordering of the method invocation self-object class.

therefore, for super(cls, instance) ( isinstance(instance, cls) must be True ), the method is selected from the following class in instance.__class__.__mro__ starting with instance.__class__ .

for super(cls0, cls1) ( issubclass(cls1, cls0) must be True ), the method is selected from the following class in cls1.__mro__ starting from cls0

in both cases, if this method is not implemented by the next class in the MRO chain, the search will be skipped until it finds a class with a specific method.

0
source share

All Articles