An explanation of super usage in Python

Source: Internet
Author: User
first, the problem of the discovery and proposed

In the Python class method, to invoke a method of the parent class, before Python 2.2, the usual notation is code Snippet 1:

Code Snippet 1:
Copy the Code code as follows:


Class A:
def __init__ (self):
Print "Enter A"
Print "Leave A"

Class B (A):
def __init__ (self):
Print "Enter B"
A.__init__ (self)
Print "Leave B"

>>> B = B ()

Enter B
Enter A
Leave A
Leave B


That is, use a non-binding class method (a method referenced with the class name), and in the argument list, introduce the object to be bound (self) to achieve the purpose of calling the parent class.

The disadvantage of this is that when the parent class of a subclass changes (such as when the parent class of Class B is changed from A to C), the entire class definition must be traversed, replacing all the class names of the unbound methods, such as code Snippet 2,

Code Snippet 2:
Copy the Code code as follows:


Class B (c): # A--C
def __init__ (self):
Print "Enter B"
C.__init__ (self) # A--C
Print "Leave B"


If the code is simple, this change might be acceptable. But if the amount of code is large, such a modification could be catastrophic.

So, since Python 2.2, Python has added a keyword super to solve this problem. The following is an official document description of Python 2.3:
Copy the Code code as follows:


Super (type[, Object-or-type])

Return the superclass of type. If The second argument is omitted the Super object
Returned is unbound. If The second argument is an object, isinstance (obj, type)
Must be true. If The second argument is a type, Issubclass (type2, type) must be
True. Super () only works for New-style classes.

A typical use for calling a cooperative superclass method is:

Class C (B):
def meth (self, arg):
Super (C, self). Meth (ARG)

New in version 2.2.


From the description, you can rewrite class B as code Snippet 3:

Code Snippet 3:
Copy the Code code as follows:


Class A (object): # A must be New-style class
def __init__ (self):
Print "Enter A"
Print "Leave A"

Class B (c): # A--C
def __init__ (self):
Print "Enter B"
Super (B, self). __init__ ()
Print "Leave B"

Try to execute the same code above, the result is consistent, but only one place to modify the code, to minimize the maintenance of the code, is a good use. So in our development process, the Super keyword is heavily used and has been performing well.

In our impression, for super (b, self). __init__ () is the understanding that super (b, self) first finds the parent of B (that is, Class A), and then converts the object of Class B to the object of Class A (in a way that has not been refined in any way, Ashamed), and then the "converted" Class A object calls its own __init__ function. Given that there is only a mechanism for specifying subclasses in Super, in a multi-inheritance class definition, we usually keep using a method similar to code Snippet 1.

One day a colleague designed a relatively complex class architecture (let's not care if this class system is designed to be reasonable, just consider this example as a topic), Code Snippet 4:

Code Snippet 4:
Copy the Code code as follows:


Class A (object):
def __init__ (self):
Print "Enter A"
Print "Leave A"

Class B (object):
def __init__ (self):
Print "Enter B"
Print "Leave B"

Class C (A):
def __init__ (self):
Print "Enter C"
Super (C, self). __init__ ()
Print "Leave C"

Class D (A):
def __init__ (self):
Print "Enter D"
Super (D, self). __init__ ()
Print "Leave D"
Class E (B, C):
def __init__ (self):
Print "Enter E"
B.__init__ (self)
C.__init__ (self)
Print "Leave E"

Class F (E, D):
def __init__ (self):
Print "Enter F"
E.__init__ (self)
D.__init__ (self)
Print "Leave F"

>>> f = f ()

Enter F
Enter E
Enter B
Leave B
Enter C
Enter D
Enter A
Leave A
Leave D
Leave C
Leave E
Enter D
Enter A
Leave A
Leave D
Leave F


Obviously, the initialization functions of Class A and Class D are called repeatedly 2 times, which is not the result we expect! The result we expect is that at most only the initialization function of Class A is called 2 times-in fact, this is a problem that a multi-inheritance class system must face. We draw the class system of code snippet 4, such as:
Copy CodeThe code is as follows:


Object
| /
| A
| / |
B C D
/ / |
E |
/ |
F


In our understanding of super, we can see that when invoking the initialization function of Class C, the initialization function of Class A is called, but the initialization function of Class D is actually called. Good one weird problem!

Ii. entering the world of Python's source code

We tried to rewrite the function calls in code Snippet 4, but we didn't get the results we wanted, which forced us to start wondering whether we had a problem with super's understanding.

We re-read the official Python documentation, and as you can see, the official documentation does not have a detailed explanation of the rationale. Go to the web to search, indeed someone has found the same problem, and discussed in some forums, but there seems to be no substantive answer. Since, without the footprints of our predecessors, we had to go into Python's source world to trace the root of the problem.

We examined the source code for Python 2.3 (it is estimated that Python 2.4 may have the same source code). First, search for the keyword "super". The only thing to find is a sentence in bltinmodule.c:
Copy the Code code as follows:


Setbuiltin ("Super", &pysuper_type);


So, we have the first misunderstanding of super: super is not a function, but a class (Pysuper_type).

The definition of Pysuper_type was found in typeobject.c:

Code Snippet 5:
Copy the Code code as follows:


Pytypeobject Pysuper_type = {
Pyobject_head_init (&pytype_type)
0,/* ob_size */
"Super",/* tp_name */
sizeof (Superobject),/* tp_basicsize */
0,/* tp_itemsize */
/* Methods */
Super_dealloc,/* Tp_dealloc */
0,/* tp_print */
0,/* tp_getattr */
0,/* tp_setattr */
0,/* tp_compare */
SUPER_REPR,/* TP_REPR */
0,/* tp_as_number */
0,/* tp_as_sequence */
0,/* tp_as_mapping */
0,/* tp_hash */
0,/* tp_call */
0,/* TP_STR */
Super_getattro,/* Tp_getattro */
0,/* tp_setattro */
0,/* tp_as_buffer */
Py_tpflags_default | py_tpflags_have_gc |
Py_tpflags_basetype,/* tp_flags */
Super_doc,/* Tp_doc */
Super_traverse,/* tp_traverse */
0,/* tp_clear */
0,/* tp_richcompare */
0,/* tp_weaklistoffset */
0,/* tp_iter */
0,/* tp_iternext */
0,/* tp_methods */
Super_members,/* tp_members */
0,/* tp_getset */
0,/* tp_base */
0,/* tp_dict */
Super_descr_get,/* tp_descr_get */
0,/* tp_descr_set */
0,/* tp_dictoffset */
Super_init,/* tp_init */
Pytype_genericalloc,/* Tp_alloc */
Pytype_genericnew,/* tp_new */
Pyobject_gc_del,/* tp_free */
};


As you can tell from code Snippet 5, the Super class only rewrites a few methods, the most important of which are: Tp_dealloc,tp_getattro,tp_traverse,tp_init.

Then look at the definition of Superobject:

Code Snippet 6:
Copy the Code code as follows:


typedef struct {
Pyobject_head
Pytypeobject *type;
Pyobject *obj;
Pytypeobject *obj_type;
} Superobject;


You can see from code snippet 6 that the data members of Superobject have only 3 pointers (references to 3 objects). To know what each of these 3 objects represents, you must examine the definition of super_init:

Code Snippet 7:
Copy the Code code as follows:


static int
Super_init (Pyobject *self, Pyobject *args, Pyobject *kwds)
{
Superobject *SU = (Superobject *) self;
Pytypeobject *type;
Pyobject *obj = NULL;
Pytypeobject *obj_type = NULL;

if (! Pyarg_parsetuple (args, "o!| O:super ", &pytype_type, &type, &obj))
return-1;
if (obj = = py_none)
obj = NULL;
if (obj! = NULL) {
Obj_type = Supercheck (type, obj);
if (Obj_type = = NULL)
return-1;
Py_incref (obj);
}
Py_incref (type);
Su->type = type;
Su->obj = obj;
Su->obj_type = Obj_type;
return 0;
}


As you can see from the code, Super_init first interprets the incoming parameter list by pyarg_parsetuple, respectively, in the type and obj variables. The optional parameter, obj, is then tested by Supercheck to be legal and to obtain the concrete class type of the instance obj. Finally, record the type, obj, and Obj_type. In other words, the Super object simply makes some records, and does not make any conversion operations.

The pointcut for finding the problem is why the super call in Class C switches to the initialization function of Class D. The conditional breakpoint is added to the super_init and the Python code behind it is tracked. Finally goes to the Super_getattro function--the search operation that corresponds to the Super object when it accesses the name __init__.

Code Snippet 8 (omit some extraneous code and add some comments):
Copy the Code code as follows:


Static Pyobject *
Super_getattro (Pyobject *self, Pyobject *name)
{
Superobject *SU = (Superobject *) self;
int skip = Su->obj_type = = NULL;
......
if (!skip) {
Pyobject *mro, *res, *tmp, *dict;
Pytypeobject *starttype;
Descrgetfunc F;
int i, n;

StartType = su->obj_type; Get the start of a search: the Obj_type of Super objects
MRO = starttype->tp_mro; Get the MRO for class
......
for (i = 0; i < n; i++) {//Search MRO, locate type in MRO
if ((Pyobject *) (su->type) = = Pytuple_get_item (MRO, i))
Break
}
i++; Switch to the next class in MRO
res = NULL;
for (; i < n; i++) {//search for specified names in various namespaces after MRO
TMP = Pytuple_get_item (MRO, i);
if (Pytype_check (TMP))
Dict = ((Pytypeobject *) tmp)->tp_dict;
else if (Pyclass_check (TMP))
Dict = ((Pyclassobject *) tmp)->cl_dict;
Else
Continue
res = Pydict_getitem (dict, name);
if (res! = NULL) {
Py_incref (RES);
f = res->ob_type->tp_descr_get;
if (f! = NULL) {
TMP = f (res, su->obj,
(Pyobject *) starttype);
Py_decref (RES);
res = TMP;
}
return res;
}
}
}
Return pyobject_genericgetattr (self, name);
}


As you can see from the code, super objects are actually based on the MRO of class instances when searching for namespaces. So what is MRO? Find official documents, which are:
Copy CodeThe code is as follows:


pyobject* Tp_mro
Tuple containing the expanded set of base types, starting with the type itself and
Ending with object, in Method Resolution Order.

This field was not inherited; It is calculated fresh by Pytype_ready ().


That is, the MRO records a sequence of class types for all the base classes of a class. Review the MRO records and discover that there are 7 elements, and 7 class names are:
Copy CodeThe code is as follows:


F E B C D A Object


This explains why super (C, self) is used in c.__init__. __INIT__ () invokes the initialization function of Class D.

Let's rewrite code snippet 4 to:

Code Snippet 9:
Copy the Code code as follows:


Class A (object):
def __init__ (self):
Print "Enter A"
Super (A, self). __init__ () # New
Print "Leave A"

Class B (object):
def __init__ (self):
Print "Enter B"
Super (B, self). __init__ () # New
Print "Leave B"

Class C (A):
def __init__ (self):
Print "Enter C"
Super (C, self). __init__ ()
Print "Leave C"

Class D (A):
def __init__ (self):
Print "Enter D"
Super (D, self). __init__ ()
Print "Leave D"
Class E (B, C):
def __init__ (self):
Print "Enter E"
Super (E, self). __init__ () # change
Print "Leave E"

Class F (E, D):
def __init__ (self):
Print "Enter F"
Super (F, self). __init__ () # change
Print "Leave F"

>>> f = f ()

Enter F
Enter E
Enter B
Enter C
Enter D
Enter A
Leave A
Leave D
Leave C
Leave B
Leave E
Leave F


Obviously, the initialization of F not only completes the invocation of all the parent classes, but also guarantees that the initialization function of each parent class is called only once.

Iii. continuation of the discussion

We re-look at the class system diagram above, if each class as a graph of a node, each from the subclass to the parent of the direct inheritance relationship as a directed edge, then the system diagram will become a directed graph. The order of the MRO can not be found to be exactly a topological sort sequence of the graph.

Thus, we get another result--python is how to deal with multiple inheritance. Traditional object-oriented programming languages (such as C + +), which support multiple inheritance, are implemented by virtual inheritance to implement multiple calls to the constructors of the parent class in multiple inheritance, while Python is handled by the MRO approach.

But this gives us a problem: for the writer who provides the class system, he does not know how the user will use his class system, that is, incorrect follow-up classes may lead to errors in the original class system, and such errors are very covert and difficult to find.

Iv. Summary

1. Super is not a function, it is a class name, like Super (B, self) actually called the Super class initialization function,
produced a Super object;
2. The Super class initialization function does not do anything special, but simply records the class type and the concrete instance;
3. Super (B, self). The invocation of Func is not a Func function that invokes the parent class of the current class;
4. Python's multi-inheritance class ensures that functions of each parent class are called through the MRO method, and that each parent class function
Call only once (if each class uses Super);
5. Mixing super and unbound functions is a risky behavior, which can cause the parent class function that should be called to not call or a
A parent class function is called multiple times.

  • Related Article

    Contact Us

    The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

    If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

    A Free Trial That Lets You Build Big!

    Start building with 50+ products and up to 12 months usage for Elastic Compute Service

    • Sales Support

      1 on 1 presale consultation

    • After-Sales Support

      24/7 Technical Support 6 Free Tickets per Quarter Faster Response

    • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.