Research on the Super usage of Python

Source: Internet
Author: User

Address: http://blog.csdn.net/johnsonguo/article/details/585193

I. Problem Discovery and Proposal

In the method of a python class, you need to call a method of the parent class. Before Python 2.2CodeSection 1:

Code Segment 1:

 Class  A:  Def   _ Init __  (Self ):  Print   "  Enter  "          Print   "  Leave  " Class  B ():  Def   _ Init __  (Self ):  Print   "  Enter B  "  A.  _ Init __ (Self) #  Display the initialization function that calls the parent class          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:

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.

 

Change to super

Super Example 1:

 Class  Base (object ):  Def   _ Init __  (Self ): Print ( "  Base created  "  )  Class  Childa (base ):  Def   _ Init __  (Self): base.  _ Init __  (Self)  Class  Childb (base ):  Def   _ Init __ (Self): Super ().  _ Init __  ()  Print (Childa (), childb ())

The help document provides the classic method used by super

 
ClassC (B ):DefMethod (self, ARG): Super (). Method (ARG)#This does the same thing:#Super (C, self). Method (ARG)

 

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:

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:

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:

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:

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:

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:

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:

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:

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 ):

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:

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:

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:

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. Supports multi-inheritance of traditional object-orientedProgramA language (such as C ++) is a problem where constructor of the parent class in multiple inheritance is called multiple times through virtual inheritance, while python is handled 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.

 

Note: I think this mechanism is similar to the polymorphism mechanism in object-oriented programming. This is just the initialization function. Other functions can also be called using super.

 

Attached the explanation of what is new style class

 

New-Style Class

Any class which inherits fromObject. This includes des all built-in types likeListAndDict. Only new-style classes can use Python's newer, versatile features like_ Slots __, Descriptors, properties, and_ Getattribute __().

That is, the class. Object object that must inherit from the object is such an object:

Return a new featureless object.ObjectIs a base for all new style classes. It has the methods that are common to all instances of new style classes.

An object has a series of methods that are common to all instantiated new style classes.

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.