私はPython 2.5を実行しているので、この質問はPython 3.に当てはまらない場合があります。3。多重継承を使用してダイヤモンドクラス階層を作成し、最も派生したクラスPythonは正しいこと(TM)を実行します。これは、最も派生したクラスのコンストラクタ、次に左から右にリストされている親クラス、そして祖父母のコンストラクタを呼び出します。 Pythonの [〜#〜] mro [〜#〜] はよく知っていますが、これは私の質問ではありません。superから返されたオブジェクトが実際にsuperの呼び出しと通信する方法に興味があります親クラスは正しい順序です。次のコード例を検討してください:
_#!/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の存在をどのように伝えますか?スーパーユースの後続の呼び出しで保存された情報は、オブジェクト自体に保存されていますか?もしそうなら、なぜセルフではなくスーパーではないのですか?
編集:Jekkeは、superはクラスのインスタンスではなくクラスの属性であるため、self.superではないことを正しく指摘しました。概念的にはこれは理にかなっていますが、実際にはスーパーもクラスの属性ではありません!これをインタープリターでテストするには、BがAから継承する2つのクラスAとBを作成し、dir(B)
を呼び出します。 super
または___super__
_属性はありません。
以下に、私がこれまでに望んでいたよりも詳細かつ正確にあなたの質問に答えるリンクをいくつか提供しました。ただし、時間を節約するために、私もあなたの質問に自分の言葉で答えます。私はそれをポイントに入れます-
__mro__
_属性があります。__init__
_)。例の説明
_ MRO: D,B,C,A,object
_
super(D, self).__init__()
が呼び出されます。 isinstance(self、D)=> TrueDの右側のクラスのMROでnext methodを検索します。
_B.__init__
_が見つかり、呼び出されました
_B.__init__
_はsuper(B, self).__init__()
を呼び出します。
isinstance(self、B)=> False
isinstance(self、D)=> True
したがって、MROは同じですが、検索はBの右側に続きます。つまり、C、A、オブジェクトが1つずつ検索されます。次に見つかった___init__
_が呼び出されます。
等々。
superの説明
http://www.python.org/download/releases/2.2.3/descrintro/#cooperation
superを使用するときに注意すること
http://fuhm.net/super-harmful/
Python MROアルゴリズム:
http://www.python.org/download/releases/2.3/mro/
superのドキュメント:
http://docs.python.org/library/functions.html
このページの下部には、superの[Nice]セクションがあります
http://docstore.mik.ua/orelly/other/python/0596001886_pythonian-chp-5-sect-2.html
これで問題が解決することを願っています。
コードをこれに変更すると、説明がわかると思います(おそらく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は気の利いたものですが、使用できません 。
推測するだけです:
4つのメソッドすべてのself
は、同じオブジェクト、つまりクラスD
を参照します。そのため、B.__init__()
では、super(B,self)
への呼び出しはself
のダイヤモンドの祖先全体を認識しており、「後」からメソッドをフェッチする必要がありますB
。この場合は、C
クラスです。
super()
はfullクラス階層を認識しています。これは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__
にあります。
Jacobの答えは問題を理解する方法を示し、batbratの答えは詳細とhrrの問題を直接示しています。
彼らがあなたの質問から(少なくとも明示的には)カバーしない1つのことはこの点です:
ただし、Bのinit関数でsuperの呼び出しをコメント化すると、AもCのinit関数も呼び出されません。
それを理解するには、以下のように、JacobのコードをAのinitにスタックを出力するように変更します。
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
はC
のベースクラスではないため、B
の行super(B, self).__init__()
が実際にC.__init__()
を呼び出しているのを見ると、少し意外です。
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()
これは、super (B, self)
が 'Bのベースクラスバージョンの__init__
を呼び出していないために発生します。代わりに、 'B
の__init__
に存在し、そのような属性を持つself
の右側の最初のクラスで__mro__
を呼び出すです。
したがって、Bのinit関数でのsuperの呼び出しをコメント化の場合、メソッドスタックはB.__init__
で停止し、C
またはA
に到達することはありません。
要約する:
self
は常にインスタンスへの参照であり、その__mro__
および__class__
は一定のままです__mro__
で現在のクラスの右側にあるクラスを探すメソッドを見つけます。 __mro__
は一定のままであるため、ツリーやグラフとしてではなく、リストとして検索されます。その最後の点で、MROのアルゴリズムの完全な名前はC3 superclass linearizationであることに注意してください。つまり、その構造をリストにフラット化します。さまざまなsuper()
呼び出しが発生した場合、それらは効果的にそのリストを繰り返します。