SoFunction
Updated on 2024-11-12

Example analysis of the usage of the super function in Python

This article example describes the use of the super function in Python. Shared for your reference, as follows:

This is one of those high profile functions that you can look like an expert by describing it too much in the python handbook 23333. But it's actually pretty important. In a nutshell.superA function is a method that calls the next parent class (superclass) and returns an instance of that parent class. The concept of next is described later in the MRO table.

The help is described as follows.

super(type, obj) -> bound super object; requires isinstance(obj, type)
super(type) -> unbound super object
super(type, type2) -> bound super object; requires issubclass(type2, type)
Typical use to call a cooperative superclass method:
class C(B):
    def meth(self, arg):
        super(C, self).meth(arg)

It follows that super can be used in three ways, the first argument is always the class that calls the parent, the second argument can be missing (returning an object of the unbound parent), an instance object or a subclass of the class. In the end, it's always an instance of the parent class (bound or unbound) that is returned. In Python 3, the super function has an additional use in the form of directsuper(), equivalent tosuper(type, first parameter), this first parameter is the usual incomingselfinstance itself. This is also the way it is written in py2.

Also, in py2, super only supports new-style classes (that is, classes that inherit from object).

Why call the parent class?

In class inheritance, if a method is redefined, it overrides the corresponding method of the same name in the parent class. By calling instances of the parent class, it is possible to implement the functionality of the parent class in the subclasses at the same time. For example.

# Should be new-class based on object in python2.
class A(object):
 def __init__(self):
  print "enter A"
  print "leave A"
class B(A):
 def __init__(self):
  print "enter B"
  super(B, self).__init__()
  print "leave B"
>>> b = B()
enter B
enter A
leave A
leave B

This is accomplished by calling thesuperGet an instance of the parent class so that you can implement the initialization function of that instance. This is too often used in practice (because of the need to inherit functionality from the parent class, but also to have new functionality).

Differences in calling directly using the parent class

In fact, the abovesuperThe function method can also be written like this.

class A(object):
 def __init__(self):
  print "enter A"
  print "leave A"
class B(A):
 def __init__(self):
  print "enter B"
  A.__init__(self)
  print "leave B"

It is also possible to call a method of a parent class by using the parent's class name directly. At least in the above example they work the same way now. This method is also the only way to call a parent method in old-style classes (which don't have super).

Calling a method by its parent class name is common and intuitive. However, there is a difference between the effect of calling a method by its parent class name and that of calling a method by its super. For example.

class A(object):
 def __init__(self):
  print "enter A"
  print "leave A"
class B(A):
 def __init__(self):
  print "enter B"
  A.__init__(self)
  print "leave B"
class C(A):
 def __init__(self):
  print "enter C"
  A.__init__(self)
  print "leave C"
class D(B,C):
 def __init__(self):
  print "enter D"
  B.__init__(self)
  C.__init__(self)
  print "leave D"
>>> d=D()
enter D
enter B
enter A
leave A
leave B
enter C
enter A
leave A
leave C
leave D

As you can see, A's initialization function is executed twice. Since we're implementing the initialization functions for B and C at the same time, it's inevitable that they'll be called twice.

But what if it's rewritten as super?

class A(object):
 def __init__(self):
  print "enter A"
  print "leave A"
class B(A):
 def __init__(self):
  print "enter B"
  super(B,self).__init__()
  print "leave B"
class C(A):
 def __init__(self):
  print "enter C"
  super(C,self).__init__()
  print "leave C"
class D(B,C):
 def __init__(self):
  print "enter D"
  super(D,self).__init__()
  print "leave D"
>>> d=D()
enter D
enter B
enter C
enter A
leave A
leave C
leave B
leave D

It will be noticed that all the parent classes ABC are executed only once, and not twice as the initialization of A was executed before.

Then, another strange thing was found: the execution of the parent class is in the order of BCA and is all in and all out. This is an MRO table problem, and will be discussed later.

If there is no multiple inheritance, thesuperIt's pretty much the same as calling a method through its parent class. However, super has another advantage: when B inherits from A, it is written asA.__init__, if refactored as needed, all to be inherited fromEThen you'll have to change it all at once! It's a pain in the ass and prone to errors! Instead of usingsuper()Then you don't have to change them one by one (just change them in the class definition)

Anyway, as you can see.superIt's not that simple.

MRO Table

What is MRO? It can be tuned out by.

>>> () # or d.__class__.mro() or D.__class__.mro(D) 
[D, B, C, A, object]
>>> ()
[B, A, object]
>>> help()
#Docstring:
#mro() -> list
#return a type's method resolution order
#Type:  method_descriptor

MRO is the method resolution ordering of a class, which is actually the ordering of the methods inherited from the parent class (the class inheritance ordering works fine).

What does this table do? First, let's find out what Super actually does.

def super(cls, inst):
 mro = inst.__class__.mro()
 return mro[(cls) + 1]

In other words, the super method actually calls the next class in the MRO table of cls. In the case of simple one-line single inheritance, it's parent-> parent class by parent class. But for multiple inheritance, you have to follow the order in the MRO table. Take the above call to D as an example.

Initialization of d

-> D (into D) super(D,self)
-> parent class B (into B) super(B,self)
-> parent class C (into C) super(C,self)
-> parent parent A (into A) (out of A) # if continue super(A,self) -> object (stopped)
-> (exit C)
-> (Exit B)
-> (Exit D)

So, the superclass initialization function in the MRO table is executed only once!

So, what is the order of MRO? You can refer to the official descriptionThe Python 2.3 Method Resolution Order . Basically, calculate the MRO for each class (in order from parent to child) and merge into a line. The following rules apply.

In MRO, the base class always appears after the derived class, and if there is more than one base class, the relative order of the base classes remains the same. This principle consists of two points.

1. Base classes always come after derived classes
2. The order of inheritance at the time of class definition affects the relative order.

If there is an inheritance (Python's super usage in detail):

    object
     /   \
    /      A
   |     /   \
  B-1  C-2   D-2
    \   /    /
     E-1    /
        \  /
          F

Then the MRO is.F -> E -> B -> C -> D -> A -> object

How do you explain it?

According to the official method, it is.

L(O) = O
L(B) = B O
L(A) = A O
L(C) = C A O
L(D) = D A O
L(E) = E + merge(L(B),L(C))
  = E + merge(BO,CAO)
  = E + B + merge(O,CAO)
  = E + B + C + merge(O,AO)
  = E + B + C + A + merge(O,O)
  = E B C A O
L(F) = F + merge(L(E),L(D))
  = F + merge(EBCAO,DAO)
  = F + EBC + merge(AO,DAO)
  = F + EBC + D + merge(AO,AO)
  = F EBC D AO

It looks complicated... But it's followed.In MRO, the base class always appears after the derived class, and if there is more than one base class, the relative order of the base classes remains the same.So, personally, I think it's fair to say.

  • first find outlongestDeepest line of successionF->E->C->A->object. (because necessarilyBase classes always appear after derived classes)
  • Similar to depth-first, to determine the rest of the order.F->E->B->obj, F->D->A-object
  • If there is more than one base class, the relative order of the base classes remains the same, similar to merge, which prioritizes the first items. So line up these routes: (FEBO, FECAO, FDAO)
  • F->E->B->objand E(B,C) determines that B is in front of C. SoF->E->B->C->A->obj (Equivalent toF+merge(EBO,ECAO)).
  • F->D->A-objectAnd F(E,D) determines that D comes after E, so D comes before A after E. Because of the relative order, it is equivalent toFE+merge(BCAO, DAO), soFE BC D AO

More can be found in

  • Raymond Hettinger (used form a nominal expression)Python's super() considered super! (A supposedly classic discussion)
  • James Knight'sPython's Super Considered Harmful
  • Py3 cookbook: 8.7 Calling parent class methods/zh_CN/latest/c08/p07_calling_method_on_parent_class.html
  • The correct understanding and usage of the super function in Python programming.

For those interested in Python-related content, check out this site's feature: thePython Object-Oriented Programming Introductory and Advanced Tutorials》、《Python Data Structures and Algorithms Tutorial》、《Summary of Python function usage tips》、《Summary of Python string manipulation techniques》、《Summary of Python coding manipulation techniquesand thePython introductory and advanced classic tutorials

I hope that what I have said in this article will help you in Python programming.