钻石问题--多继承python--方法只被调用一次--但如何调用?

问题描述 投票:0回答:1

在下面这个例子中,方法 m 课堂上 A 只被调用一次。

我理解这是一个功能,这是Pythonic的方式来解决这个问题,其中的 A's m 方法会在这种类似钻石的继承方案中被调用两次(如果它是以天真的方式实现的)。

这一切在这里都有描述。https:/www.python-course.eupython3_multiple_inheritance.php

(1)但在幕后......他们是如何实现这种行为的,即该班的 A's m 方法只被调用一次?简单地问:在执行过程中,哪一行被 "跳过"--是哪一行?#1 或行 # 2 ?

谁能给我更多的启示?我从来没有认真使用过多重继承,因为我主要用Java编程。所以我真的很好奇这里的这个方案,更具体的说是对它背后的内部工作原理很好奇。

注意:我只是想了解一下Python中的大致工作原理,而不是真的了解这里的每一个微小的细节。

(2)如果我想(在这个同样的场景下,并且由于某些原因) A's 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()
python python-3.x multiple-inheritance
1个回答
5
投票

这与 方法解析顺序你所链接的文章已经提供了一些见解(更多信息来自于 此条也):

问题是超级函数如何作出决定。它是如何决定使用哪个类的呢?正如我们已经提到的,它使用所谓的方法解析顺序(MRO)。它是基于 C3超类线性化 算法。这被称为线性化,因为树结构被分解成线性顺序。可以使用mro方法来创建这个列表。

>>> 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())与当前的类(即 B, C...)来确定哪个是下一个要解析方法的类。

super() 实际上使用了两个参数,但是当调用零参数时 课内,它是隐式传递的。

还要注意的是,除了零参数的形式之外, super() 不限于在方法内部使用。两参数形式准确地指定了参数,并做了相应的引用。零参数形式只在类定义内部使用,因为编译器会填写必要的细节,以正确检索被定义的类,以及访问普通方法的当前实例。

准确地说,在 B.m()జజజజజజజజజజజజజజజజజజజజజజజజజజజజజజజజజజజజజజజ super() 调用实际转化为。

super(B, x).m()
# because the self being passed at the time is instance of D, which is x

该调用在 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())

结果就是:

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基于调用的范围解析的想法,在这个范围内 super() 是,相对于从的调用。初始对象.


这里还有一个有趣的小例子,可以更详细地演示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())

结果:

# 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'>]
© www.soinside.com 2019 - 2024. All rights reserved.