Summary of Usage of Python super of Function

  • 2021-11-10 09:54:03
  • OfStack

Directory 1. Purpose of super () 2. Understand the basics of super 3. Typical Usage 3.1 Single Inheritance Problem 3.2 Single Inheritance Problem Extension 3.3 Repeat Call Problem 3.4 super (type) Problem

1. Uses of super ()

Before we understand the super () function, we first need to know what is the purpose of super ()?

It is mainly used to call the methods of the parent class in subclasses. Used in multi-inheritance problems, to solve the search order (MRO), repeated calls (diamond inheritance) and other issues.

2. Understand the basics of super

Syntax format:


super([type[, object-or-type]])

Function description:

Returns a proxy object that delegates the method call to the parent or sibling class of type.

Parameter description:

type-Class, optional parameter. object-or-type-Object or class, 1 is generally self, optional parameter.

Return value:

super object-Proxy object.

help Help Information:


>>> help(super)
Help on class super in module builtins:
 
class super(object)
 |  super() -> same as super(__class__, <first argument>)
 |  super(type) -> unbound super object
 |  super(type, obj) -> bound super object; requires isinstance(obj, type)
 |  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().meth(arg)
 |  This works for class methods too:
 |  class C(B):
 |      @classmethod
 |      def cmeth(cls, arg):
 |          super().cmeth(arg)
 
... ...
super is a class that inherits from object, and calling the super () function is actually an instantiation of the super class. According to the official document, the object returned by the super () function-super object-is a proxy object. super () has four combinations of parameters. super () applies to static methods of classes.

3. Typical usage

3.1 Single inheritance problem

First, let's look at a basic example of a subclass calling a parent class method:


>>> class A:
        def funxx(self):
            print(" Execute  A  In  funxx  Method  ... ...")
 
        
>>> class B(A):
        def funxx(self):
            A.funxx(self)       #  Invoke the method with the same name in the parent class by the class name, self  Parameter representation  B  Instance object of class  b
            print(" Execute  B  In  funxx  Method  ... ...")
 
        
>>> b = B()
>>> b.funxx()
 Execute  A  In  funxx  Method  ... ...
 Execute  B  In  funxx  Method  ... ...
A subclass B inherited from A is defined, and the funxx () method is overridden in B. funxx () in B is an extension of funxx () in A. Because it expands the function of funxx () method of A class, it still retains the original function, that is, it calls the method with the same name of the parent class in the subclass B to realize the original function. In the above example, the method of the same name in the A class is called by the A class name, and the first parameter self actually passes the instance b of the B class.

Use the super () function to implement the call of the parent class method:


>>> class A:
        def funxx(self):
            print(" Execute  A  In  funxx  Method  ... ...")
 
        
>>> class B(A):
        def funxx(self):
            super().funxx()
            print(" Execute  B  In  funxx  Method  ... ...")
 
		
>>> b = B()
>>> b.funxx()
 Execute  A  In  funxx  Method  ... ...
 Execute  B  In  funxx  Method  ... ...
Through the result of execution, we can see that the result of implementing and calling ordinary class names is 1. In a class hierarchy with single inheritance, super makes code more maintainable by referring to parent classes without explicitly specifying their names. (Official document description) That is to say, in the subclass, the parent class method is no longer called with the parent class name, but a proxy object is used to call the parent class method, so that when the parent class name changes or the inheritance relationship changes, there is no need to modify every call place.

3.2 Extension of Single Inheritance Problem

In help() Use in the class is also described in the help information of super() The form without parameters is equivalent to super(__class__, <first argument>) This form. This is also about Python 2. x and Python 3. x super() The difference.

Rewrite the code of the previous single inheritance problem:


>>> class A:
        def funxx(self):
            print(" Execute  A  In  funxx  Method  ... ...")
 
		
>>> class B(A):
        def funxx(self):
	        super(B, self).funxx()
	        print(" Execute  B  In  funxx  Method  ... ...")
 
		
>>> b = B()
>>> b.funxx()
 Execute  A  In  funxx  Method  ... ...
 Execute  B  In  funxx  Method  ... ...
Basic invocation method A.funxx(self) Where self refers to the instance object b. It is described in language as: instance object b calls method through A class name funxx() . Official description: Returns a proxy object that delegates method calls to the parent or sibling class of type. Described in language: The proxy object super calls its methods through the parent class or sibling class of type. We found that super selects which parent class method to call through parameter setting. The second parameter gives MRO (method resolution order), that is, the order in which the target method is searched, and the first parameter gives the scope of the target method. For example super(B, self) The first parameter is B, the second parameter self is the instance b, and the inheritance order of its class (MRO) is B → A → object. Therefore, when calling, it is searched in the parent class B, A, and if the target method cannot be found, it will be searched in object, which is one layer higher.

Example:


class A:
    pass
 
 
class B(A):
    pass
 
 
class C(A):
    def funxx(self):
        print(" Find  funxx()  Located at  C  Medium ...")
 
 
class D(A):
    pass
 
 
class E(B, C):
    pass
 
 
class F(E, D):
    def funff(self):
        print(" Execute  F  In  funff()...")
        super(E, self).funxx()
 
        
print(f"F  Class  MRO : {F.__mro__}")
f = F()
f.funff()

Run results:


F  Class  MRO : (<class '__main__.F'>, <class '__main__.E'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.D'>, <class '__main__.A'>, <class 'object'>)
 Execute  F  In  funff()...
 Find  funxx()  Located at  C  Medium ...
We can see that MRO of F class is F → E → B → C → D → A → object. super() The first argument of the function is E, and the target is to call B, the parent class of E funxx() Method, unfortunately not found in B class, found in C, the sibling of B class, meets the requirements.

3.3 Repeat Call Problem

Repeated call problem is also called diamond inheritance problem or diamond chart problem.

Let's take a look at the common calling method in:


>>> class A:
        def __init__(self):
            print(" Print properties  a")
 
	    
>>> class B(A):
        def __init__(self):
            print(" Print properties  b")
            A.__init__(self)
 
	    
>>> class C(A):
        def __init__(self):
            print(" Print properties  c")
            A.__init__(self)
 
	    
>>> class D(B, C):
        def __init__(self):
            print(" Print properties  d")
            B.__init__(self)
            C.__init__(self)
 
	    
>>> d = D()
 Print properties  d
 Print properties  b
 Print properties  a
 Print properties  c
 Print properties  a
Because both B and C inherit from A, the constructor of A is executed twice when D is instantiated. This is the so-called repeated call problem. Obviously, we only need to call once, and repeated calls will only cause waste of resources.

Next, we use the super () function to call:


>>> class A:
        def __init__(self):
            print(" Print properties  a")
 
	    
>>> class B(A):
        def __init__(self):
            print(" Print properties  b")
            super().__init__()                # super()  Equivalent to  super(B, self)
 
	    
>>> class C(A):
        def __init__(self):
            print(" Print properties  c")
            super().__init__()                # super()  Equivalent to  super(C, self)
 
	    
>>> class D(B, C):
        def __init__(self):
            print(" Print properties  d")
            super(D, self).__init__()
 
	    
>>> d = D()
 Print properties  d
 Print properties  b
 Print properties  c
 Print properties  a
Looking at the output, we found that although the problem of repeated calls was solved, the order of the output results seemed to be different from what we thought. Our habitual thinking is to execute D class first __init__() Method of the B class, and then call the __init__() Method of the parent class A is called in the construction method of the B class __init_() Method, and then call the C class __init_() Method, which also calls the parent class A __init__() Method. Therefore, the results of execution should be: print attribute d, print attribute b, print attribute a, and print attribute c. Why is the result not what we thought? First, we need to know that the second parameter self in D class is d, an instance of D, and its MRO is D → B → C → A → object. So in the D class super() Function generates the proxy object of d when it calls the parent class B __init__() B's super() The second parameter of is super object in D, and the MRO provided by it is still D → B → C → A → object. That is to say, in B super() Called in its upper level C __init__() Instead of in A __init__() . Therefore, the results of execution are: print attribute d, print attribute b, print attribute c, and print attribute a.

3.4 super (type) Issues


>>> class A:
	    def funxx(self):
		    print("...A...")
 
		
>>> class B(A):
	    def funxx(self):
		    print("...B...")
 
		
>>> sa = super(B)
>>> print(sa)
<super: <class 'B'>, NULL>
>>> print(type(sa))
<class 'super'>

You can see that super (type) returned an invalid object or an unbound super object.


Related articles: