小能豆

调用父类__init__并进行多重继承,正确的方法是什么?

python

假设我有一个多重继承场景:

class A(object):
    # code for A here

class B(object):
    # code for B here

class C(A, B):
    def __init__(self):
        # What's the right code to write here to ensure 
        # A.__init__ and B.__init__ get called?

有两种典型的编写C方法__init__

  1. (老式)ParentClass.__init__(self)
  2. (新式)super(DerivedClass, self).__init__()

但是,无论哪种情况,如果父类(AB不遵循相同的约定,那么代码将无法正常工作(有些可能会被遗漏,或者被多次调用)。

那么正确的方法是什么?说“只要保持一致,遵循其中一个”很容易,但如果AB来自第三方库,那该怎么办?有没有一种方法可以确保所有父类构造函数都被调用(并且以正确的顺序,并且只调用一次)?

编辑:看看我的意思,如果我这样做:

class A(object):
    def __init__(self):
        print("Entering A")
        super(A, self).__init__()
        print("Leaving A")

class B(object):
    def __init__(self):
        print("Entering B")
        super(B, self).__init__()
        print("Leaving B")

class C(A, B):
    def __init__(self):
        print("Entering C")
        A.__init__(self)
        B.__init__(self)
        print("Leaving C")

然后我得到:

Entering C
Entering A
Entering B
Leaving B
Leaving A
Entering B
Leaving B
Leaving C

请注意,Binit 被调用了两次。如果我这样做:

class A(object):
    def __init__(self):
        print("Entering A")
        print("Leaving A")

class B(object):
    def __init__(self):
        print("Entering B")
        super(B, self).__init__()
        print("Leaving B")

class C(A, B):
    def __init__(self):
        print("Entering C")
        super(C, self).__init__()
        print("Leaving C")

然后我得到:

Entering C
Entering A
Leaving A
Leaving C

请注意,B的 init 永远不会被调用。因此,除非我知道/控制我继承自的类的 init(AB),否则我无法为正在编写的类做出安全的选择(C)。


阅读 38

收藏
2024-07-12

共1个答案

小能豆

为了正确处理多重继承场景中的初始化,尤其是在处理无法控制或假设方法行为的第三方库时__init__,您应该使用super()并确保层次结构中的所有类super()也使用。

这里的关键是方法解析顺序 (MRO),Python 使用它来确定__init__方法的调用顺序。通过使用super(),Python 可以遵循 MRO,并__init__仅以正确的顺序调用每个类的方法一次。

下面是一个如何正确初始化类的示例:

class A(object):
    def __init__(self, *args, **kwargs):
        print("Entering A")
        super(A, self).__init__(*args, **kwargs)
        print("Leaving A")

class B(object):
    def __init__(self, *args, **kwargs):
        print("Entering B")
        super(B, self).__init__(*args, **kwargs)
        print("Leaving B")

class C(A, B):
    def __init__(self, *args, **kwargs):
        print("Entering C")
        super(C, self).__init__(*args, **kwargs)
        print("Leaving C")

# Test the classes
c = C()

运行此代码时,您应该得到以下输出:

Entering C
Entering A
Entering B
Leaving B
Leaving A
Leaving C

以下是具体发生情况:

  1. C.__init__叫做。
  2. super(C, self).__init__被称为,它紧随 MRO 之后。
  3. 根据 MRO,A.__init__接下来被调用。
  4. super(A, self).__init__被称为 within A.__init__,它遵循 MRO 到B.__init__
  5. super(B, self).__init__在 内调用B.__init__,它遵循 MRO 到object.__init__,即所有类的根。
  6. 这些__init__方法按照其调用的相反顺序返回。

这样,所有__init__方法都只会以正确的顺序调用一次。使用super()可确保遵守 MRO,并避免多次初始化或错过初始化的问题。

2024-07-12