Usage of super in Python

Source: Internet
Author: User
This article mainly introduces the usage of super in Python. This article describes the discovery and proposal of super, the implementation of super in the source code world of Python, and the continuation of the discussion of super, for more information, see I. Problem Discovery and Proposal

In the method of a Python class, to call a method of the parent class, before Python 2.2, the usual method is as follows:

Code Segment 1:

The Code is as follows:


Class:
Def _ init _ (self ):
Print "enter"
Print "leave"

Class B ():
Def _ init _ (self ):
Print "enter B"
A. _ init _ (self)
Print "leave B"

>>> B = B ()

Enter B
Enter
Leave
Leave B


That is, use non-bound class methods (Methods referenced by class names), and introduce the objects to be bound (self) in the parameter list to call the parent class.

The disadvantage is that when the parent class of A subclass changes (for example, when the parent class of Class B changes from A to C), the entire class definition must be traversed, replace all the class names of non-bound methods, such as code snippet 2,

Code Segment 2:

The Code is 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, such changes may be acceptable. However, if the code volume is large, such modifications may be disastrous.

Therefore, since Python 2.2, Python has added a keyword "super" to solve this problem. The official documentation for Python 2.3 is as follows:

The Code is 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, we can rewrite Class B as code Segment 3:

Code Segment 3:

The Code is as follows:


Class A (object): # A must be new-style class
Def _ init _ (self ):
Print "enter"
Print "leave"

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, and the results are consistent, but there is only one modified code, which minimizes the maintenance of the Code, which is a good usage. Therefore, in our development process, the super keyword is widely used and has always performed well.

In our impression, for super (B, self ). _ init _ () is as follows: super (B, self) first finds the parent class of B (Class ), then, convert the self object of Class B to the object of Class A (in some way, I have never studied what the method is ), the converted Class A object then calls its own _ init _ function. Considering that super only specifies the subclass mechanism, in the multi-inheritance class definition, we usually retain the method similar to code segment 1.

One day, a colleague designed a relatively complex class architecture (we should not care whether the class system design is reasonable, but just take this example as a question to study ), code example 4:

Code segment 4:

The Code is as follows:


Class A (object ):
Def _ init _ (self ):
Print "enter"
Print "leave"

Class B (object ):
Def _ init _ (self ):
Print "enter B"
Print "leave B"

Class C ():
Def _ init _ (self ):
Print "enter C"
Super (C, self). _ init __()
Print "leave C"

Class D ():
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
Leave
Leave D
Leave C
Leave E
Enter D
Enter
Leave
Leave D
Leave F


Obviously, the initialization functions of Class A and Class D are repeatedly called twice, which is not the expected result! The expected result is that only the initialization function of Class A is called twice. In fact, this is A problem that must be faced by the multi-inheritance class system. We will draw out the class system of code segment 4, such:

The Code is as follows:


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


According to our understanding of super, we can see that when calling Class C's initialization function, we should call class A's initialization function, but actually called Class D's initialization function. A strange problem!

Ii. Entering the Python source code world

We tried to rewrite the function call in code segment 4, but we didn't get the expected result. This had to make us start to doubt whether our understanding of super was wrong.

We have read the official Python documentation again. As you can see, the official documentation does not provide detailed principles. Some people found the same problem and discussed it in some forums, but they did not seem to have any substantive answers. Since we do not have the footprints of our predecessors, we have to go into the Python source code world and trace the root cause of the problem.

We examine the source code of Python 2.3 (it is estimated that the source code of Python 2.4 may be similar ). First, search for the keyword "super ". The only one found in bltinmodule. c is:

The Code is 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 PySuper_Type definition is found in typeobject. c:

Code segment 5:

The Code is 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 */
};


From code segment 5, we can know that the super class only modifies several methods, including: tp_dealloc, tp_getattro, tp_traverse, and tp_init.

Let's look at the definition of superobject:

Code segment 6:

The Code is as follows:


Typedef struct {
PyObject_HEAD
PyTypeObject * type;
PyObject * obj;
PyTypeObject * obj_type;
} Superobject;


From code segment 6, we can see that the data member of superobject has only three pointers (references of three objects ). To know what these three objects represent, you must examine the definition of super_init:

Code segment 7:

The Code is 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;
}


From the code, we can see that super_init first interprets the passed parameter list through PyArg_ParseTuple and stores them in the type and obj variables respectively. Then, use supercheck to test whether the optional parameter obj is valid and obtain the specific class type of the Instance obj. Finally, record type, obj, and obj_type. That is to say, the super object simply makes some records and does not perform any conversion operations.

The starting point for finding the problem is why the super call in Class C switches to the initialization function of class D. Therefore, add a conditional breakpoint in super_init and track the subsequent Python code. Finally, enter the super_getattro function-the search operation corresponding to the super Object Access name__ init.

Code Segment 8 (some irrelevant code is omitted and some comments are added ):

The Code is 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 search start point: obj_type of the super object
Mro = starttype-> tp_mro; // obtain the class mro
......
For (I = 0; I <n; I ++) {// search for the 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 the specified name in each namespace 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 );
}


From the code, we can see that the super object is actually based on the mro of the class instance when searching for the namespace. So what is mro? Search for official documents, including:

The 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 is not inherited; it is calculated fresh by PyType_Ready ().


That is to say, mro records the sequence of class types of all base classes of a class. Check the mro records and find that there are 7 elements and the seven class names are:

The Code is as follows:


F e B C D A object


This explains why super (C, self). _ init _ () is used in C. _ init _ to call the initialization function of class D.

We rewrite code segment 4:

Code segment 9:

The Code is as follows:


Class A (object ):
Def _ init _ (self ):
Print "enter"
Super (A, self). _ init _ () # new
Print "leave"

Class B (object ):
Def _ init _ (self ):
Print "enter B"
Super (B, self). _ init _ () # new
Print "leave B"

Class C ():
Def _ init _ (self ):
Print "enter C"
Super (C, self). _ init __()
Print "leave C"

Class D ():
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
Leave
Leave D
Leave C
Leave B
Leave E
Leave F


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

Iii. Continuation Discussion

Let's re-look at the class system diagram above. If we look at each class as a node and the direct inheritance relationship between each subclass and its parent class as a directed edge, then the system diagram will become a directed graph. It cannot be found that the mro sequence is just a topological sorting sequence of the directed graph.

In this way, we get another result: How does Python process multi-inheritance. The traditional object-oriented programming language (such as C ++) that supports multi-inheritance is a problem in which the constructor of the parent class in Multi-inheritance is called multiple times through virtual inheritance, python is processed by mro.

But this gives us a difficult problem: for the compiler who provides the class system, he does not know how the user will use his class system. That is to say, the following classes are incorrect, it may lead to errors of the original class system, and such errors are very concealed and difficult to be found.

Iv. Summary

1. super is not a function. It is a class name. For example, super (B, self) actually calls the initialization function of the super class,
Generates a super object;
2. The super class initialization function does not perform any special operations, but simply records the class type and specific instance;
3. The call of super (B, self). func is not used to call the func function of the parent class of the current class;
4. The multiple inheritance classes of Python use mro to ensure that the functions of each parent class are called one by one, and each parent class function is also guaranteed.
Only one call (if each class uses super );
5. mixing super classes and unbound functions is a dangerous action, which may cause the parent class function to be called not to be called or
Parent functions are called multiple times.

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.