1. ホーム
  2. パイソン

[解決済み】多重継承で親クラスの__init__を呼び出す、正しい方法は?

2022-04-02 04:45:26

質問

多重継承のシナリオがあるとします。

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?

の書き方には、2つの典型的なアプローチがあります。 C 's __init__ :

  1. (旧スタイル) ParentClass.__init__(self)
  2. (新型) super(DerivedClass, self).__init__()

ただし、いずれの場合も、親クラス( AB ) が同じ規則に従わない場合、このコードは正しく動作しません。 (いくつかは見逃されたり、何度も呼ばれたりすることがあります)。

では、正しい方法とは何でしょうか? 一貫性を保つために、どちらかに従いなさいというのは簡単ですが、もし A または B がサードパーティライブラリから来たものである場合、どうすればよいのでしょうか? すべての親クラスのコンストラクタを確実に呼び出す(しかも正しい順序で、一度だけ)アプローチはあるのでしょうか?

編集:もしそうなら、私が言いたいことを見るために。

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

なお B のinitは2回呼ばれます。 そうすると

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 ).

解決方法は?

どちらの方法でも問題ありません。 を使用する方法 super() は、サブクラスに対してより大きな柔軟性をもたらします。

直接呼出し方式では C.__init__ の両方を呼び出すことができます。 A.__init__B.__init__ .

を使用する場合 super() を使用する場合、クラスは協調的な多重継承ができるように設計する必要があります。 C を呼び出す super を呼び出し、それが A のコードも呼び出されます。 super を呼び出す。 B のコードです。 参照 http://rhettinger.wordpress.com/2011/05/26/super-considered-super で何ができるのか、詳しくは super .

[後日編集された回答質問]

ということで、自分が使っているクラスのinitがわからない/コントロールできない限りは AとBを継承したクラスでは、安全な選択をすることができません。 を書きます(C)。

参照記事では、この状況に対処するために、ラッパー・クラスを周囲に追加する方法を紹介しています。 AB . 非協力的なクラスを組み込む方法" と題されたセクションに、実践的な例があります。

多重継承がもっと簡単で、CarクラスとAirplaneクラスを組み合わせてFlyingCarを作ることができればと思うかもしれませんが、現実には別々に設計されたコンポーネントがシームレスにフィットするまでにアダプターやラッパーが必要になることがよくあります :-)。

もうひとつ、多重継承による機能合成に不満がある場合は、コンポジションを使って、どのメソッドがどの場面で呼ばれるかを完全にコントロールすることも可能です。