ホームページ >バックエンド開発 >Python チュートリアル >Pythonの多重継承について詳しく解説

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

大家讲道理
大家讲道理オリジナル
2016-11-08 10:15:151001ブラウズ

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) を見つけて、次にクラスを配置します。 B into オブジェクト self がクラス A のオブジェクトに変換され、「変換された」クラス 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 left 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 left A Leave D Leave C Leave B Leave E Leave F

F の初期化により、すべての親クラスの呼び出しが完了するだけでなく、各親クラスの初期化関数が 1 回だけ呼び出されることがわかります。

概要

1. Superは関数ではなく、クラス名です super(B, self)という形式は、実際にはスーパークラスの初期化関数

を呼び出し、スーパーオブジェクトを生成します。 class 初期化関数は特別な操作を行わず、単にクラスの型と特定のインスタンスを記録します

3. super(B, self).func への呼び出しは、現在の親クラスの func 関数を呼び出すために使用されません。 class;

4. Python の多重継承クラスは、各親クラスの関数が 1 つずつ呼び出されることを保証し、各親クラスの関数が 1 回だけ呼び出されることを保証します (各クラスが super を使用する場合)。スーパークラスと非バインディング関数は危険な動作であり、呼び出されるはずの親クラス関数が呼び出されなかったり、親クラス関数が複数回呼び出されたりする可能性があります。

さらに詳しい質問: ご覧のとおり、F.__mro__ を出力すると、内部の要素の順序は F E B C D A オブジェクトであることがわかります。これは F の基本クラスの検索順序です。なぜこのようになるのかについてです。 order、および Python の組み込み多重継承 シーケンスの実装方法、これには mro シーケンスの実装が含まれます


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