class A(object): # A must be new-style class def __init__(self): print "enter A" print "leave A" class B(C): # A --> C def __init__(self): print "enter B" super(B, self).__init__() print "leave B"
私たちの印象では、 super(B, self).__init__() は次のように理解されます。 super(B, self) は、まず B の親クラス (つまりクラス A) を見つけ、次にクラスのオブジェクト self を変換します。クラス A のオブジェクトに対して B を呼び出し、クラス A の「変換された」オブジェクトが独自の __init__ 関数を呼び出します。
ある日、同僚が比較的複雑なクラス アーキテクチャを設計しました (クラス アーキテクチャが合理的に設計されているかどうかは気にせず、この例をトピックとして学習してください)。コードは次のとおりです
コード セグメント 4:
class A(object): def __init__(self): print "enter A" print "leave A" class B(object): def __init__(self): print "enter B" print "leave B" class C(A): def __init__(self): print "enter C" super(C, self).__init__() print "leave C" class D(A): def __init__(self): print "enter D" super(D, self).__init__() print "leave D" class E(B, C): def __init__(self): print "enter E" B.__init__(self) C.__init__(self) print "leave E" class F(E, D): def __init__(self): print "enter F" E.__init__(self) D.__init__(self) print "leave F"
f = F()、結果は次のようになります:
enter F enter E enter B Leave B enter C enter D enter A Leave A Leave D Leave C Leave E enter D enter A Leave A Leave D Leave F
明らかに、クラス A とクラス D の初期化関数が 2 回呼び出されます。これは期待した結果ではありません。予想される結果は、クラス A の初期化関数が最大 2 回呼び出されるということです。実際、これは多重継承クラス システムが直面しなければならない問題です。以下に示すように、コード セグメント 4 のクラス システムを描画します。図からわかるように、クラス C の初期化関数が呼び出された場合、クラス A の初期化関数は次のようにする必要があります。と呼ばれていますが、実際にはクラス D の初期化関数が呼び出されます。なんて奇妙な質問でしょう!
言い換えると、mro はクラスのすべての基本クラスのクラス型シーケンスを記録します。 mro のレコードを見ると 7 つの要素が含まれており、その 7 つのクラス名は次のとおりです:
F E B C D A object
C.__init__ で super(C, self).__init__() を使用するとクラス D が呼び出される理由がこれで説明されています。初期化関数。 ???
コード セグメント 4 を次のように書き換えます:
コード セグメント 5:
class A(object): def __init__(self): print "enter A" super(A, self).__init__() # new print "leave A" class B(object): def __init__(self): print "enter B" super(B, self).__init__() # new print "leave B" class C(A): def __init__(self): print "enter C" super(C, self).__init__() print "leave C" class D(A): def __init__(self): print "enter D" super(D, self).__init__() print "leave D" class E(B, C): def __init__(self): print "enter E" super(E, self).__init__() # change print "leave E" class F(E, D): def __init__(self): print "enter F" super(F, self).__init__() # change print "leave F"f = F()、実行結果: enter F enter E enter B enter C enter D enter A Leave A Leave D Leave C Leave B Leave E Leave FF の初期化により、すべての親クラスの呼び出しが完了するだけでなく、各親クラスの初期化関数が 1 回だけ呼び出されることがわかります。
概要
1. super は関数ではなく、クラス名です super(B, self) という形式は、実際にはスーパークラスの初期化関数
を呼び出し、スーパーオブジェクト
3. super(B, self).func への呼び出しは、現在のクラスの親クラスの func 関数を呼び出すために使用されません。 ;
4. Python の多重継承クラスは、各親クラスの関数が 1 つずつ呼び出されることを保証するために、また各親クラスの関数が 1 回だけ呼び出されることを保証します (各クラスが super を使用する場合)。クラスと非 Binding 関数は危険な動作であり、呼び出されるはずの親クラス関数が呼び出されなかったり、親クラス関数が複数回呼び出されたりする可能性があります。
さらに詳しい質問: ご覧のとおり、F.__mro__ を出力すると、内部の要素の順序は F E B C D A オブジェクトであることがわかります。これは F の基本クラスの検索順序です。この順序と Python の組み込み多重継承 シーケンスの実装には、mro シーケンスの実装が含まれます。Python 2.3 以降のバージョンでは、次のブログで紹介する C3 と呼ばれるアルゴリズムが使用されます。