Python advanced-MRO and super in succession
Write in front
If not specifically stated, the following are based on Python3
Summary
This article describes Python
how an inheritance relationship can be super()
returned by calling the "parent class" method, super(Type, CurrentClass)
returning the proxy for the CurrentClass
MRO
Type
next class, and how to design the Python
class for proper initialization.
1. Parent class method invocation in single inheritance
In inheritance, it is necessary to call the parent class method. There are many scenarios for calling the parent class method:
For example, you must call the parent class's constructor __init__
to properly initialize the parent class instance property so that the subclass instance object can inherit the instance property of the parent class instance object;
If you need to override the parent class method, there is sometimes no need to completely abandon the parent class implementation, just add some implementations before and after the parent class implementation, and ultimately call the parent class method
Single inheritance is the simplest inheritance relationship, multiple inheritance is too complex and error-prone to use. Therefore, some high-level languages completely abandon multiple inheritance, only support single inheritance; some high-level languages support multiple inheritance, but it is not recommended to use multiple inheritance. Python
also, in the inability to fully grasp the multi-inheritance, it is best not to use, single inheritance to meet most of the needs.
1.1 Non-binding method invocation
The difference between a binding method and a non-binding method see: Python Basics-Classes
There are two classes of inheritance relationships, as follows:
Class D (object):d EF test (self):p rint (' Test in D ') class C (d):d EF-Test (self):p rint (' Test-in-C ') d.test (self)
It is now required to C
invoke the implementation of the parent class in the function of the subclass test
D
test
. The most straightforward way we can think of is to refer directly to the D
function member of the class object test
:
Class D (object):d EF test (self):p rint (' Test in D ') class C (d):d EF test (self):p rint (' Test in C ')
Try testing:
c = C () c.test ()
Output
Test in CTest in D
It seems that the non-binding approach does satisfy the need for the current invocation of the parent class method.
1.2 Builtin function Super
Refer to Python tutorial for a description of super:super(\[type\[, object-or-type\]\])
Return a proxy object, delegates method calls to a parent or sibling class of type. This is useful for accessing inherited methods that has been overridden in a class. The search order is same as, used by GetAttr () except, the type itself is skipped.
super
The function returns type
the proxy object for the parent class or sibling method call of the delegate class. super
used to invoke a parent class method that has been overridden in a subclass. The search order of the method is the same as the getattr()
function, except that the parameter class type
itself is ignored.
1.3 Binding Method Invocation
Invoking the parent class method with a binding method naturally cannot explicitly pass in the parameter current object ( self
). Now the super
function can be scoped to the proxy of the parent class, because there is only one parent class in the single-inheritance neutron class, so the parent class is explicit, and we fully understand which parent class method is called:
Class D (object):d EF test (self):p rint (' Test in D ') class C (d):d EF test (self):p rint (' Test in C ') super (). Test () # super (C, SE LF). The omitted form of test ()
2. In-depth super
In fact, the super
proxy object returned by the function is one bultin class super
, and as its name implies, the class super
proxies the parent class of the subclass. In a single-inheritance relationship, the class of the super
proxy is easy to find, that is, the only parent of the subclass, but in a multi-inheritance relationship, super
there is a possibility to proxy subclasses of siblings in addition to the parent class that can proxy subclasses.
2.1 Complex Multiple inheritance
In a multi-inheritance relationship, inheritance relationships can be quite complex.
Class D (object): def test (self):p rint (' Test in D ') class C (d): def test (self):p rint (' Test in C ') class B (d): def test (self):p rint (' Test in B ') class A (b, C):p
The class A
inheritance hierarchy is as follows:
Object | D /\ B C \/ A
A
there is a diamond structure in the inheritance relationship of a class, that A
is, you can reach a parent class from a class through multiple paths D
.
If you now require A
a method that calls the " parent class " In a class test
, you need a test
search-parsing order of the methods to determine exactly which method to invoke B,C或D
test
.
2.2 Method parsing order (MRO)
The search order of the methods proposed above test
is the method parsing order.
Depth first
Python
In legacy classes, the method parsing order is depth-first, and multiple parent classes are left-to-right.
Breadth First
Python
In the new class, the method parsing order is breadth-first, and multiple parent classes are left-to-right.
So the above parsing order is: A -> B -> C -> D -> object
.
Python
, the properties of the class __mro__
show the method search order, which can be called mro()
or directly referenced to __mro__
get the search order:
Print (A.MRO ()) print (a.__mro__)
Output
[<class ' __main__. A ';, <class ' __main__. B ';, <class ' __main__. C ';, <class ' __main__. D ';, <class ' object ';] (<class ' __main__. A ';, <class ' __main__. B ';, <class ' __main__. C ';, <class ' __main__. D ';, <class ' object ' >)
So
A = A () a.test () # Output:test in B
Changes in MRO
Even in the same class, the back-and-forth relationship between positions in different MRO is different. such as the following classes:
Class D (object): def test (self):p rint (' Test in D ') class C (d): def test (self):p rint (' Test in C ') class B (d): def test (self):p rint (' Test in B ')
B
the inheritance hierarchy for a class is:
Object | D /\ C B
B
MRO for class:B -> D -> object
A
MRO for comparison classes:A -> B -> C -> D -> object
In the same class B
, location relationships are different in two different MRO. It can be said that by adding new subclasses to existing inheritance relationships, new classes are introduced into the MRO and the parsing order is changed.
As you can imagine, the B
super
method that is actually called in different MRO is different in the class's Test by calling the parent class method.
As follows:
Class D (object): def test (self):p rint (' Test in D ') class C (d): def test (self):p rint (' Test in C ') super (). Test () Class B (D): def test (self):p rint (' Test in B ') super (). Test () class A (b, C):p ASSB = B () b.test () print (' ========== ') a = a () A.test ()
Output
Test in Btest in D==========test in Btest in CTest in D
Because of the subclass in the original class relationship, the method called in the method B
C
A
B
test
super
test
has changed, the original call is the method of its parent class, D
test
now calls its sibling class C
the test
method.
From here it can be seen super
that the parent class is not always the surrogate subclass, and it is possible to proxy its sibling class.
Therefore, in the design of the multi-inheritance relationship of the class system, pay special attention to this point.
2.3 See Super Method again
Method super([type[, object-or-type]])
that returns the proxy for the type
parent class or sibling class.
If the second argument is omitted, the returned super
object is not bound to a certain one MRO
:
If the second argument is an object, then it isinstance(obj, type)
must be True
;
If the second argument is a type, then it issubclass(type2, type)
must be True
, that is, the second parameter type is a subclass of the first parameter type.
super
when the second parameter of a function is present, its implementation is probably as follows:
Def super (CLS, inst): MRO = Inst.__class__.mro () # always the most derived Classreturn Mro[mro.index (CLS) + 1]
Obviously, the super
second parameter is returned in the corresponding class MRO
list, the first parameter type
of the next class of the proxy. Therefore, it is necessary to require that the first parameter be type
present in the second parameter class MRO
, only the first parameter class is the parent class of the class of the second argument, which is guaranteed.
super()
super
The function is required to have parameters and there is no function without arguments super
. Called in a class definition, super()
is an ellipsis, and the interpreter fills in the necessary parameters. The first parameter of the fill is the current class, and the second parameter is self
:
Super () = Super (Current_class, self)
Therefore, super()
this notation is destined to be used only in the class definition.
Now look at the inheritance relationship above:
Class D (object):d EF test (self):p rint (' Test in D '), Class C (d):d EF test (self):p rint (' Test in C ') # super (). Test () # is equivalent to the following notation s Uper (C, self). Test () # Returns the Agent Class B (D):d EF test (self):p rint (' Test in B ') # super (). Test () # for the MRO of the corresponding class, class C. The following notation is equivalent to super (b, self). Test () # Returns the Agent Class A (b, C) for the next class of class B in the MRO of the self-corresponding class,:p the
So:
b = B () b.test () # Based on Class B MRO (B->d->object), super () Proxy in class B dprint (' ========== ') a = A () a.test () # MRO based on Class A (a->b- >c->d->object), super () agent C in class B