ホームページ  >  記事  >  バックエンド開発  >  Pythonの多重継承について詳しく解説

Pythonの多重継承について詳しく解説

高洛峰
高洛峰オリジナル
2016-10-18 10:19:221436ブラウズ

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 F

F の初期化により、すべての親クラスの呼び出しが完了するだけでなく、各親クラスの初期化関数が 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 と呼ばれるアルゴリズムが使用されます。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。