Speaking ofsuper, everyone might think it's very simple—isn't it just used to call parent class methods? If it were really that simple, this article wouldn't exist. Let me explain it in detail.
Conventions
Before we start, let's agree on the Python version used in this article. By default, we use Python 3, which means all classes defined in this article are new-style classes. If you're using Python 2, remember to inherit fromobject:
# 默认, Python 3
class A:
pass
# Python 2
class A(object):
pass
Another difference between Python 3 and Python 2 is that Python 3 can directly usesuper().xxxinstead ofsuper(Class, self).xxx :
# 默认,Python 3
class B(A):
def add(self, x):
super().add(x)
# Python 2
class B(A):
def add(self, x):
super(B, self).add(x)
So, if you're using Python 2, remember to replace the article'ssuper()withsuepr(Class, self) 。
If there are any other cases incompatible with Python 2, I will note them in the article.
Single Inheritance
In single inheritance,superjust as everyone thinks, super is mainly used to call parent class methods.
class A:
def __init__(self):
self.n = 2
def add(self, m):
print('self is {0} @A.add'.format(self))
self.n += m
class B(A):
def __init__(self):
self.n = 3
def add(self, m):
print('self is {0} @B.add'.format(self))
super().add(m)
self.n += 3
What do you think the value of b.nwill be after executing the following code?
b = B() b.add(2) print(b.n)
The execution result is as follows:
self is <__main__.B object at 0x106c49b38> @B.add self is <__main__.B object at 0x106c49b38> @A.add 8
This result illustrates two points:
- 1、super().add(m)It indeed calls the parent class A'saddmethod.
- 2、super().add(m)When calling the parent class methoddef add(self, m), at this time, in the parent classselfis not an instance of the parent class but an instance of the child class, sob.add(2)the result after is5rather than4 。
I wonder if this result is the same as what you expected? Next, let's look at an example of multiple inheritance.
Multiple Inheritance
This time, let's define anotherclass C, and aclass D:
class C(A):
def __init__(self):
self.n = 4
def add(self, m):
print('self is {0} @C.add'.format(self))
super().add(m)
self.n += 4
class D(B, C):
def __init__(self):
self.n = 5
def add(self, m):
print('self is {0} @D.add'.format(self))
super().add(m)
self.n += 5
What does the following code output?
d = D() d.add(2) print(d.n)
This time the output is as follows:
self is <__main__.D object at 0x10ce10e48> @D.add self is <__main__.D object at 0x10ce10e48> @B.add self is <__main__.D object at 0x10ce10e48> @C.add self is <__main__.D object at 0x10ce10e48> @A.add 19
Did you get it right? You might think the output of the above code would be similar to:
self is <__main__.D object at 0x10ce10e48> @D.add self is <__main__.D object at 0x10ce10e48> @B.add self is <__main__.D object at 0x10ce10e48> @A.add 15
Why is it different from what was expected? Let's take a look together atsuperthe mystery of.
super Is a Class
When we callsuper(), we are actually instantiating asuperclass. You read that right,superis a class, neither a keyword nor a function or other data structure:
>>> class A: pass ... >>> s = super(A) >>> type(s) <class 'super'> >>>
In most cases,supercontains two very important pieces of information: an MRO and a class in that MRO. When called in the following waysuper:
super(a_type, obj)
MROrefers totype(obj)'s MRO, and the class in the MRO isa_type, and at the same timeisinstance(obj, a_type) == True 。
When called like this:
super(type1, type2)
The MRO refers totype2ofMRO, MROthe class in istype1, and at the same timeissubclass(type2, type1) == True 。
So,super()what does it actually do? Simply put: provide aMROand aMROclass inC , super()will return an object that looks up methods fromMROMediumCthe classes after it.
That is, the lookup is not like a regular method that searches allMROclasses, but rather searchesMRO's tail.
For example, there is aMRO:
[A, B, C, D, E, object]
the following call:
super(C, A).foo()
superwill only search fromConward, that is: onlyDorEorobjectwill be searched forfoomethod.
How super Works in Multiple Inheritance
Let's go back to the earlier
d = D() d.add(2) print(d.n)
Now you might have some idea why the output is
self is <__main__.D object at 0x10ce10e48> @D.add self is <__main__.D object at 0x10ce10e48> @B.add self is <__main__.D object at 0x10ce10e48> @C.add self is <__main__.D object at 0x10ce10e48> @A.add 19
, right? ;)
Let's analyze it in detail below:
DofMROis:[D, B, C, A, object] 。 Note:You can viewD.mro()(Python 2 usesD.__mro__) to viewDofMROinformation)
-
The detailed code analysis is as follows:
class A: def __init__(self): self.n = 2 def add(self, m): # 第四步 # 来自 D.add 中的 super # self == d, self.n == d.n == 5 print('self is {0} @A.add'.format(self)) self.n += m # d.n == 7 class B(A): def __init__(self): self.n = 3 def add(self, m): # 第二步 # 来自 D.add 中的 super # self == d, self.n == d.n == 5 print('self is {0} @B.add'.format(self)) # 等价于 suepr(B, self).add(m) # self 的 MRO 是 [D, B, C, A, object] # 从 B 之后的 [C, A, object] 中查找 add 方法 super().add(m) # 第六步 # d.n = 11 self.n += 3 # d.n = 14 class C(A): def __init__(self): self.n = 4 def add(self, m): # 第三步 # 来自 B.add 中的 super # self == d, self.n == d.n == 5 print('self is {0} @C.add'.format(self)) # 等价于 suepr(C, self).add(m) # self 的 MRO 是 [D, B, C, A, object] # 从 C 之后的 [A, object] 中查找 add 方法 super().add(m) # 第五步 # d.n = 7 self.n += 4 # d.n = 11 class D(B, C): def __init__(self): self.n = 5 def add(self, m): # 第一步 print('self is {0} @D.add'.format(self)) # 等价于 super(D, self).add(m) # self 的 MRO 是 [D, B, C, A, object] # 从 D 之后的 [B, C, A, object] 中查找 add 方法 super().add(m) # 第七步 # d.n = 14 self.n += 5 # self.n = 19 d = D() d.add(2) print(d.n)The call procedure diagram is as follows:
D.mro() == [D, B, C, A, object] d = D() d.n == 5 d.add(2) class D(B, C): class B(A): class C(A): class A: def add(self, m): def add(self, m): def add(self, m): def add(self, m): super().add(m) 1.---> super().add(m) 2.---> super().add(m) 3.---> self.n += m self.n += 5 <------6. self.n += 3 <----5. self.n += 4 <----4. <--| (14+5=19) (11+3=14) (7+4=11) (5+2=7)
Now you know why d.n is 19 after d.add(2), right? ;)
Article source: https://mozillazg.com/2016/12/python-super-is-not-as-simple-as-you-thought.html