pet*_*rov 2 python multiple-inheritance python-3.x
在下面的这个例子中,m类上的方法A只被调用一次。
我知道这是一个功能,这是解决在这种类似菱形的继承场景中A'sm方法将被调用两次(如果它以幼稚的方式实现)的问题的 Pythonic 方法。
这一切都在这里描述:https :
//www.python-course.eu/python3_multiple_inheritance.php
(1) 但是在幕后……他们是如何实现这种行为的,即该类A的m方法只被调用一次?!
简单地询问:在执行过程中哪一行被“跳过” - 是 line#1还是 line # 2?
有人可以对此有更多的了解吗?
我从来没有认真使用过多重继承,因为我主要用 Java 编程。所以我真的很好奇这里的这个场景,更具体地说是它背后的内部运作。
注意:我只是想大致了解一下它在 Python 中的工作原理,而不是真正了解这里的每一个细节。
(2) 如果我想(在同样的场景中并且出于某种原因)A的m方法被调用两次(或N次数取决于D我们有多少基类),同时仍然使用super(). 这可能吗?是否super()支持这种操作方式?
(3) 这只是一些树或DAG访问算法,它们会跟踪哪个类的m方法已经被访问过并且只是不访问(调用它)两次?如果是这样,那么简单地说,我猜“#2”是被跳过的行。
class A:
def m(self):
print("m of A called")
class B(A):
def m(self):
print("m of B called")
super().m() # 1
class C(A):
def m(self):
print("m of C called")
super().m() # 2
class D(B,C):
def m(self):
print("m of D called")
super().m()
if (__name__ == '__main__'):
x = D()
x.m()
Run Code Online (Sandbox Code Playgroud)
这与方法解析顺序有关,您链接的文章已经提供了一些见解(以及其他文章中的更多信息):
问题是超级函数如何做出决定。它如何决定必须使用哪个类?正如我们已经提到的,它使用所谓的方法解析顺序(MRO)。它基于C3 超类线性化算法。这称为线性化,因为树结构被分解为线性顺序。mro 方法可用于创建此列表:
Run Code Online (Sandbox Code Playgroud)>>> from super_init import A,B,C,D` >>> D.mro() [<class 'super_init.D'>, <class 'super_init.B'>, <class 'super_init.C'>, <class 'super_init.A'>, <class 'object'>]`
注意 MRO 从D> B> C> 的位置A。您认为super()只是调用当前作用域的父类 - 事实并非如此。它通过当前类(即,...)查看对象的类 MRO(即D.mro()),以确定哪个类是解析该方法的下一个类。BC
在super()实际使用两个参数,但与零个参数调用一个类里面,它是隐式传递:
另请注意,除了零参数形式外,
super()不限于使用内部方法。两个参数形式准确地指定参数并进行适当的引用。零参数形式仅在类定义中起作用,因为编译器会填写必要的详细信息以正确检索正在定义的类,以及访问普通方法的当前实例。
准确地说,在 点上B.m(),super()调用实际上转换为:
super(B, x).m()
# because the self being passed at the time is instance of D, which is x
Run Code Online (Sandbox Code Playgroud)
内的这一呼吁做出决议D.mro()从B类开始,这实际上是C不是A因为你的想象。因此,C.m()首先被称为,并且在其中,super(C, x).m()决心到A.m()和 那被称为。
之后,它解析回super()内部之后C.m(),回退到super()内部之后B.m(),以及回退到D.m()。当您添加更多行时,很容易观察到这一点:
class A:
def m(self):
print("m of A called")
class B(A):
def m(self):
print("m of B called")
print(super())
super().m() # resolves to C.m
print('B.m is complete')
class C(A):
def m(self):
print("m of C called")
print(super())
super().m() # resolves to A.m
print('C.m is complete')
class D(B,C):
def m(self):
print("m of D called")
print(super())
super().m() # resolves to B.m
print('D.m is complete')
if (__name__ == '__main__'):
x = D()
x.m()
print(D.mro())
Run Code Online (Sandbox Code Playgroud)
结果是:
Run Code Online (Sandbox Code Playgroud)m of D called <super: <class 'D'>, <D object>> m of B called <super: <class 'B'>, <D object>> m of C called <super: <class 'C'>, <D object>> m of A called C.m is complete # <-- notice how C.m is completed before B.m B.m is complete D.m is complete [<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]
所以实际上,没有任何东西会被调用两次或跳过。您只是误解了 MRO 根据范围 where super()is从调用解析的想法,而不是从初始 object调用。
这是另一个有趣的小例子,可以更详细地演示 MRO:
def print_cur_mro(cls, obj):
# helper function to show current MRO
print(f"Current MRO: {' > '.join([f'*{m.__name__}*' if m.__name__ == cls.__name__ else m.__name__ for m in type(obj).mro()])}")
class X:
def m(self):
print('m of X called')
print_cur_mro(X, self)
try:
super().a_only() # Resolves to A.a_only if called from D(), even though A is not in X inheritance
except AttributeError as exc:
# Resolves to AttributeError if not called from D()
print(type(exc), exc)
print('X.m is complete')
class A:
def m(self):
print("m of A called")
print_cur_mro(A, self)
def a_only(self):
print('a_only called')
class B(X):
def m(self):
print("m of B called")
print_cur_mro(B, self)
super().m() # Resolves to X.m
print('B.m is complete')
def b_only(self):
print('b_only called')
class C(A):
def m(self):
print("m of C called")
print_cur_mro(C, self)
try:
super().b_only() # Resolves to AttributeError if called, since A.b_only doesn't exist if from D()
except AttributeError as exc:
print(type(exc), exc)
super().m() # Resolves to A.m
print('C.m is complete')
def c_only(self):
print('c_only called, calling m of C')
C.m(self)
class D(B,C):
def m(self):
print("m of D called")
print_cur_mro(D, self)
super().c_only() # Resolves to C.c_only, since c_only doesn't exist in B or X.
super().m() # Resolves to B.m
print('D.m is complete')
if (__name__ == '__main__'):
x = D()
x.m()
print(D.mro())
x2 = X()
x2.m()
print(X.mro())
Run Code Online (Sandbox Code Playgroud)
结果:
Run Code Online (Sandbox Code Playgroud)# x.m() call: m of D called Current MRO: *D* > B > X > C > A > object c_only called, calling m of C m of C called Current MRO: D > B > X > *C* > A > object <class 'AttributeError'> 'super' object has no attribute 'b_only' m of A called Current MRO: D > B > X > C > *A* > object C.m is complete m of B called Current MRO: D > *B* > X > C > A > object m of X called Current MRO: D > B > *X* > C > A > object a_only called X.m is complete B.m is complete D.m is complete # D.mro() call: [<class '__main__.D'>, <class '__main__.B'>, <class '__main__.X'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>] # x2.m() call: m of X called Current MRO: *X* > object <class 'AttributeError'> 'super' object has no attribute 'a_only' X.m is complete # X.mro() call: [<class '__main__.X'>, <class 'object'>]