56

私はPython2.5を実行しているので、この質問はPython 3には当てはまらない可能性があります。多重継承を使用してdiamondクラス階層を作成し、最も派生したクラスのオブジェクトを作成すると、PythonはRight Thing(TM)を実行します。最も派生したクラスのコンストラクターを呼び出し、次に左から右にリストされている親クラス、次に祖父母を呼び出します。私はPythonのMROに精通しています。それは私の質問ではありません。スーパーから返されたオブジェクトが実際に親クラスのスーパーの呼び出しに正しい順序で通信する方法に興味があります。このサンプルコードを考えてみましょう。

#!/usr/bin/python

class A(object):
    def __init__(self): print "A init"

class B(A):
    def __init__(self):
        print "B init"
        super(B, self).__init__()

class C(A):
    def __init__(self):
        print "C init"
        super(C, self).__init__()

class D(B, C):
    def __init__(self):
        print "D init"
        super(D, self).__init__()

x = D()

コードは直感的なことを行い、次のように出力します。

D init
B init
C init
A init

ただし、Bのinit関数でsuperの呼び出しをコメントアウトすると、AもCのinit関数も呼び出されません。これは、Bのsuperへの呼び出しが、クラス階層全体におけるCの存在を何らかの形で認識していることを意味します。superはオーバーロードされたget演算子でプロキシオブジェクトを返すことを知っていますが、Dのinit定義でsuperによって返されるオブジェクトは、Bのinit定義でsuperによって返されるオブジェクトにCの存在をどのように伝えますか?その後のスーパーユースの呼び出しで得られる情報は、オブジェクト自体に保存されていますか?もしそうなら、なぜスーパーではなくself.superではないのですか?

編集:Jekkeは、superはクラスのインスタンスではなく、クラスの属性であるため、self.superではないことを非常に正しく指摘しました。概念的にはこれは理にかなっていますが、実際にはスーパーもクラスの属性ではありません!これは、BがAから継承する2つのクラスAとBを作成し、を呼び出すことによって、インタープリターでテストできますdir(B)superまたは__super__属性はありません。

4

5 に答える 5

34

あなたのコードをこれに変更すると、それは物事を説明すると思います(おそらく、どこにあるのかsuperを見ていると思います):B__mro__

class A(object):
    def __init__(self):
        print "A init"
        print self.__class__.__mro__

class B(A):
    def __init__(self):
        print "B init"
        print self.__class__.__mro__
        super(B, self).__init__()

class C(A):
    def __init__(self):
        print "C init"
        print self.__class__.__mro__
        super(C, self).__init__()

class D(B, C):
    def __init__(self):
        print "D init"
        print self.__class__.__mro__
        super(D, self).__init__()

x = D()

実行すると、次のように表示されます。

D init
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <type 'object'>)
B init
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <type 'object'>)
C init
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <type 'object'>)
A init
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <type 'object'>)

また、Python の Super is nifty もチェックする価値がありますが、使用することはできません

于 2009-03-03T17:17:53.700 に答える
3

super()完全なクラス階層を知っています。B の init 内では次のようになります。

>>> super(B, self)
<super: <class 'B'>, <D object>>

これにより、中心的な質問が解決されます。

D の init 定義で super によって返されたオブジェクトは、B の init 定義で super によって返されたオブジェクトに C の存在をどのように伝えますか?

つまり、B の init 定義では、selfは のインスタンスでありD、したがって の存在を伝えCます。たとえば、Cで見つけることができますtype(self).__mro__

于 2012-12-18T20:57:49.843 に答える
2

ジェイコブの答えは問題を理解する方法を示していますが、batbrat の答えは詳細を示しており、hrr の答えは要点をまっすぐに示しています。

あなたの質問から彼らがカバーしていない(少なくとも明示的ではない)ことの1つは、次の点です。

ただし、B の init 関数で super の呼び出しをコメント アウトすると、A の init 関数も C の init 関数も呼び出されません。

それを理解するには、以下のように、A の init でスタックを出力するように Jacob のコードを変更します。

import traceback

class A(object):
    def __init__(self):
        print "A init"
        print self.__class__.__mro__
        traceback.print_stack()

class B(A):
    def __init__(self):
        print "B init"
        print self.__class__.__mro__
        super(B, self).__init__()

class C(A):
    def __init__(self):
        print "C init"
        print self.__class__.__mro__
        super(C, self).__init__()

class D(B, C):
    def __init__(self):
        print "D init"
        print self.__class__.__mro__
        super(D, self).__init__()

x = D()

の基底クラスではないので、Bの行super(B, self).__init__()が実際に呼び出しているのを見るのは少し驚くべきことです。C.__init__()CB

D init
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <type 'object'>)
B init
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <type 'object'>)
C init
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <type 'object'>)
A init
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <type 'object'>)
  File "/tmp/jacobs.py", line 31, in <module>
    x = D()
  File "/tmp/jacobs.py", line 29, in __init__
    super(D, self).__init__()
  File "/tmp/jacobs.py", line 17, in __init__
    super(B, self).__init__()
  File "/tmp/jacobs.py", line 23, in __init__
    super(C, self).__init__()
  File "/tmp/jacobs.py", line 11, in __init__
    traceback.print_stack()

これは ' が 'の B のベースクラス バージョンを呼び出してsuper (B, self)いない'ために発生します。代わりに、' calling on the first class to the right to that is present on 's and that has such attribute .__init____init__Bself__mro__

したがって、B の init 関数で super の呼び出しをコメント アウトすると、メソッド スタックは で停止し、またはB.__init__に到達することはありません。CA

要約する:

  • どのクラスがそれを参照しているかに関係なく、selfは常にインスタンスへの参照であり、その__mro____class__は一定のままです
  • super() は、 上の現在のクラスの右側にあるクラスを参照するメソッドを見つけます__mro__。は一定の__mro__ままなので、ツリーやグラフとしてではなく、リストとして検索されます。

最後の点で、MRO のアルゴリズムの正式名称はC3 スーパークラス線形化であることに注意してください。つまり、その構造をリストにフラット化します。異なるsuper()呼び出しが発生すると、それらはそのリストを効果的に反復しています。

于 2017-07-09T21:48:22.220 に答える