How does Python "super" do the right thing?

I am running Python 2.5, so this question may not apply to Python 3. When you create a hierarchy of diamond classes using multiple inheritance and create an object of a derived class, Python does Right Thing (TM). It calls the constructor for the derived class, and then its parent classes, listed from left to right, and then grandfather. I am familiar with Python MRO ; This is not my question. I am curious how the object returned from super really controls to tell super calls in parent classes the correct order. Consider this code example:

#!/usr/bin/python class A(object): def __init__(self): print "A init" class B(A): def __init__(self): print "B init" super(B, self).__init__() class C(A): def __init__(self): print "C init" super(C, self).__init__() class D(B, C): def __init__(self): print "D init" super(D, self).__init__() x = D() 

The code does an intuitive thing, it prints:

 D init B init C init A init 

However, if you comment out the super call in function B init, function A or C init is not called. This means that calling B in super somehow knows about the existence of C in the general class hierarchy. I know that super returns a proxy object with an overloaded get statement, but how does the object returned by super in the definition of D init tell the existence of a C object returned by super in the definition of B init? Is the information that subsequent superuser calls stored on the object itself? If so, then why not super, but self.super?

Edit: Jekke rightly pointed out that this is not self.super, because super is an attribute of the class, not an instance of the class. Conceptually, this makes sense, but in practice, super is also not an attribute of the class! You can verify this in the interpreter by doing two classes A and B, where B inherits from A and calls dir(B) . It does not have super or __super__ .

+46
python super constructor multiple-inheritance
Mar 03 '09 at 16:45
source share
5 answers

I have provided a few links below that answer your question in more detail and more accurately than I can ever hope for. However, I will answer your question in my own words to save you time. I will put it in points -

  • super is a built-in function, not an attribute.
  • Each type (class) in Python has an __mro__ attribute, which stores the method permission order for this particular instance.
  • Each super call has the form super (type [, object-or-type]). Suppose the second attribute is the object at the moment.
  • At the starting point of a super call, the object is of type Derived ( say DC ).
  • super looks for methods that match (in your case __init__ ) the MRO classes, after the class specified as the first argument (in this case, the classes after DC).
  • When a matching method is found (for example, in class BC1 ), it is called.
    (This method should use super, so I assume that it does - see Python super is excellent, but cannot be used - the link is below) Then this method calls the MRO object class for the next method to the right of BC1 .
  • Repeat flushing until all methods are found and called.

Explanation for your example

  MRO: D,B,C,A,object 
  • super(D, self).__init__() . isinstance (self, D) => True
  • Find the following method in the MRO classes to the right of D.

    B.__init__ found and called

  • B.__init__ calls super(B, self).__init__() .

    isinstance (self, B) => False
    isinstance (self, D) => True

  • Thus, MRO is the same, but the search continues to the right of B, that is, the object C, A, the search is performed one at a time. The next __init__ found is called.

  • And so on and so forth.

Super explanation
http://www.python.org/download/releases/2.2.3/descrintro/#cooperation
What you need to observe when using super
http://fuhm.net/super-harmful/
MRO Pythons Algorithm:
http://www.python.org/download/releases/2.3/mro/
super documents:
http://docs.python.org/library/functions.html
At the bottom of this page there is a nice section about super:
http://docstore.mik.ua/orelly/other/python/0596001886_pythonian-chp-5-sect-2.html

I hope this helps to sort things out.

+14
Mar 03 '09 at 17:58
source share

Change your code to this, and I think it will explain things (presumably super looking where, say, B is in __mro__ ?):

 class A(object): def __init__(self): print "A init" print self.__class__.__mro__ class B(A): def __init__(self): print "B init" print self.__class__.__mro__ super(B, self).__init__() class C(A): def __init__(self): print "C init" print self.__class__.__mro__ super(C, self).__init__() class D(B, C): def __init__(self): print "D init" print self.__class__.__mro__ super(D, self).__init__() x = D() 

If you run it, you will see:

 D init (<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <type 'object'>) B init (<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <type 'object'>) C init (<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <type 'object'>) A init (<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <type 'object'>) 

It's also worth checking out Python Super is great, but you can't use it .

+33
Mar 03 '09 at 17:17
source share

just guessing:

self in all four methods refers to the same object, that is, to class D therefore, in B.__init__() call to super(B,self) knows the whole pedigree of the diamond self , and it must get the method from 'after' B in this case it is class C

+6
Mar 03 '09 at 17:14
source share

super() knows the complete class hierarchy. This happens inside B init:

 >>> super(B, self) <super: <class 'B'>, <D object>> 

This solves the central issue,

how does the object returned by super in the definition of D init associate the existence of C with the object returned by super in the definition of B init?

Namely, in the definition of B init, self is an instance of D and, thus, reports the existence of C For example, C can be found in type(self).__mro__ .

+3
Dec 18 '12 at 20:57
source share

Jacob's answer shows how to understand the problem, while batbrat shows the details, and hrr goes straight to the point.

One thing that they do not cover (at least not an explanation) from your question is this:

However, if you comment out the super call in function B init, function A or C init is not called.

To understand this, change Jacob's code to print the stack to A init, as shown below:

 import traceback class A(object): def __init__(self): print "A init" print self.__class__.__mro__ traceback.print_stack() class B(A): def __init__(self): print "B init" print self.__class__.__mro__ super(B, self).__init__() class C(A): def __init__(self): print "C init" print self.__class__.__mro__ super(C, self).__init__() class D(B, C): def __init__(self): print "D init" print self.__class__.__mro__ super(D, self).__init__() x = D() 

It's a little surprising to see that the line B super(B, self).__init__() actually calls C.__init__() , since C not a base class of B

 D init (<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <type 'object'>) B init (<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <type 'object'>) C init (<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <type 'object'>) A init (<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <type 'object'>) File "/tmp/jacobs.py", line 31, in <module> x = D() File "/tmp/jacobs.py", line 29, in __init__ super(D, self).__init__() File "/tmp/jacobs.py", line 17, in __init__ super(B, self).__init__() File "/tmp/jacobs.py", line 23, in __init__ super(C, self).__init__() File "/tmp/jacobs.py", line 11, in __init__ traceback.print_stack() 

This is because super (B, self) does not "invoke the B-base version of B __init__ ". Instead, it "calls __init__ in the first class to the right of B , which is present on self __mro__ and has such an attribute.

So, if you comment out the super call in the B init function, the method stack will stop at B.__init__ and never reach C or A

Summarizing:

  • No matter which class refers to it, self always refers to an instance, and its __mro__ and __class__ remain constant
  • super () finds a method that looks at classes that are to the right of the current one in __mro__ . Since the __mro__ constant remains constant, what happens is that it is searched as a list, and not as a tree or graph.

At this last point, note that the full name of the MRO algorithm is a linearization of the superclass C3. That is, it aligns this structure in the list. When various super() calls occur, they effectively repeat this list.

+1
Jul 09 '17 at 21:48
source share



All Articles