次のコードサンプルでは、super
を使用できますか、それともC
はA.foo
とB.foo
を明示的に呼び出す必要がありますか?
class A(object):
def foo(self):
print 'A.foo()'
class B(object):
def foo(self):
print 'B.foo()'
class C(A, B):
def foo(self):
print 'C.foo()'
A.foo(self)
B.foo(self)
super()
は、特定のメソッドの単一のクラスタイプのみを解決するため、複数のクラスから継承していて、両方のクラスでメソッドを呼び出したい場合は、明示的に行う必要があります。 つまりA.foo(self)
super
は確かにこの状況を対象としていますが、一貫して使用する場合にのみ機能します。基本クラスもすべてsuper
を使用しない場合、それは機能しません。メソッドがobject
にない限り、共通の基本クラスのようなものを使用してsuper
呼び出しのチェーンを終了する必要があります。
_class FooBase(object):
def foo(self): pass
class A(FooBase):
def foo(self):
super(A, self).foo()
print 'A.foo()'
class B(FooBase):
def foo(self):
super(B, self).foo()
print 'B.foo()'
class C(A, B):
def foo(self):
super(C, self).foo()
print 'C.foo()'
_
@Marcinは、なぜ共通ベースが必要なのかを尋ねます。
FooBase
を実装するがsuper()
を呼び出さないfoo
がないと、呼び出す基本メソッドがないため、super()
を呼び出す最後のクラスは属性エラーを受け取ります。
別々の基本クラスclass A(AFooBase):
とclass B(BFooBase):
があった場合、A
のsuper()
呼び出しはAFooBase
のメソッドを呼び出し、B
のメソッドは呼び出されません。ベースがすべてのクラスに共通である場合、それはメソッド解決順序の最後になり、クラスがどのように定義されていても、ベースクラスメソッドが最後に呼び出されることを確信できます。
このスレッドに貢献してくれたすべての人に感謝します。
(現在)受け入れられている答え は不正確です。正しい説明は次のようになります:super()は単一の解決に適しているだけではありません継承、しかしまた多重継承。そしてその理由は@blckknghtのコメントでよく説明されています:
基本クラスのメソッドを明示的に呼び出すことは、質問者の例のような非常に単純なシナリオでは機能しますが、基本クラス自体が共通の基本を継承し、最終的な基本クラスのメソッドが2回呼び出されたくない場合は機能しません。これは「ダイヤモンド継承」として知られており、多くの多重継承システム(C++など)にとって大きな問題です。 Pythonの協調的多重継承(super()を使用)を使用すると、多くの場合、簡単に解決できます(ただし、協調的多重継承階層の設計が簡単であるとか、常に良い考えであるとは限りません)。
@ duncanが指摘 のように、適切な方法はsuper()を使用することですが、一貫して使用します。
super
は確かにこの状況を対象としていますが、一貫して使用する場合にのみ機能します。基本クラスもすべてsuper
を使用しない場合、それは機能しません。メソッドがobject
にない限り、共通の基本クラスのようなものを使用してsuper
呼び出しのチェーンを終了する必要があります。
_class FooBase(object):
def foo(self): pass
class A(FooBase):
def foo(self):
super(A, self).foo()
print 'A.foo()'
class B(FooBase):
def foo(self):
super(B, self).foo()
print 'B.foo()'
class C(A, B):
def foo(self):
super(C, self).foo()
print 'C.foo()'
C().foo() # Run this
_
ただし、メソッドの呼び出し順序は、最初は直感的に思えない場合があることも指摘しておく価値があります。結果は次のとおりです。
_B.foo()
A.foo()
C.foo()
_
この一見奇妙な順序 実際の呼び出し順序は、MROに基づく_C, A, B
_のままです。言い換えると、
super()
は、でfooメソッドを呼び出します。"最初"「次の」スーパークラス。これは、クラスC
のメソッド解決順序(___mro__
_)に基づいています。-引用および変更 @ Manoj-Govindanの回答
_>>> C.__mro__
(<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <type 'object'>)
>>>
_
経験則として、すべての親メソッドに戻りたいが、呼び出し順序をあまり気にしない場合は、一貫してsuper()
を使用してください。それ以外の場合は、特定の順序で親メソッドを明示的に呼び出すことを選択できます。
ただし、super()の使用法と明示的な呼び出しを混在させないでください。そうしないと、 この回答 で述べたような厄介な重複が発生します。
つまり、クラスファミリ全体で一貫してsuper(...)
を使用すると、[〜#〜] all [〜#〜]同じ名前が保証されます。 [〜#〜] mro [〜#〜] の順序で1回呼び出される祖先からのメソッド。このようなcall-ALL-ancestors( call-only-the-first-candidate ではなく)の動作は、メソッドが__init__()
である場合、概念的に受け入れやすい場合があります。 このブログ投稿の例を参照 。
ただし、「MROの指示に従う」と言うのはあまり正確ではないかもしれません。どういうわけか、それは実際には常に「孫」のMROに従います。それが実際に動作しているのを見るのは魅力的です。次のプログラムの結果は、あなたが思っていたものと正確に一致しない場合があります。 _A.__mro__
_が2つの異なる呼び出しスタックでどのように同じままであるかに注意してください。ただし、super(A, self).name
とsuper(A, self).foo()
によってトリガーされた場合、A().foo()
またはC().foo()
の動作は異なります。 ]。最後に引用された結果を参照してください。
_class FooBase(object):
name = "FooBase"
def foo(self):
print(' Base.foo() begins')
print(" My name is: %s" % self.name)
print(" My super's name is not available")
print(' Base.foo() ends')
class A(FooBase):
name = "A"
def foo(self):
print(' A.foo() begins')
print(" My name is: %s" % self.name)
print(" My super's name is: %s" % super(A, self).name)
print(" A.__mro__ is %s" % str(A.__mro__))
super(A, self).foo()
print(' A.foo() ends')
class B(FooBase):
name = "B"
def foo(self):
print(' B.foo() begins')
print(" My name is: %s" % self.name)
print(" My super's name is: %s" % super(B, self).name)
print(" B.__mro__ is %s" % str(B.__mro__))
super(B, self).foo()
print(' B.foo() ends')
class C(A, B):
name = "C"
def foo(self):
print 'C.foo() begins'
print("My name is: %s" % self.name)
print("My super's name is: %s" % super(C, self).name)
print(" C.__mro__ is %s" % str(C.__mro__))
super(C, self).foo()
print('C.foo() ends')
print("We will call A.foo()")
A().foo()
print("We will call C.foo()")
C().foo() # Run this to see how each foo() is called ONLY ONCE
_
そして、Python 2.7.12からの結果は次のとおりです。
_We will call A.foo()
A.foo() begins
My name is: A
My super's name is: FooBase
A.__mro__ is (<class '__main__.A'>, <class '__main__.FooBase'>, <type 'object'>)
Base.foo() begins
My name is: A
My super's name is not available
Base.foo() ends
A.foo() ends
We will call C.foo()
C.foo() begins
My name is: C
My super's name is: A
C.__mro__ is (<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class '__main__.FooBase'>, <type 'object'>)
A.foo() begins
My name is: C
My super's name is: B
A.__mro__ is (<class '__main__.A'>, <class '__main__.FooBase'>, <type 'object'>)
B.foo() begins
My name is: C
My super's name is: FooBase
B.__mro__ is (<class '__main__.B'>, <class '__main__.FooBase'>, <type 'object'>)
Base.foo() begins
My name is: C
My super's name is not available
Base.foo() ends
B.foo() ends
A.foo() ends
C.foo() ends
_
Superは、「最初の」スーパークラスでfoo
メソッドを呼び出します。これは、クラスC
のメソッド解決順序(___mro__
_)に基づいています。
_>>> C.__mro__
(<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <type 'object'>)
>>>
_
したがって、super(C, self).foo()
を呼び出すと、_A.foo
_が呼び出されます。継承順序をclass C(B, A):
に変更すると、これは逆になります。 ___mro__
_は次のようになります。
_>>> C.__mro__
(<class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <type 'object'>)
>>>
_
この変更を行った後にsuper(C, self).foo()
を呼び出すと、B.foo()
が呼び出されます。
以下を追加した場合:
class A(object):
def foo(self):
print 'A.foo()'
class B(object):
def foo(self):
print 'B.foo()'
class C(A, B):
def foo(self):
super(C, self).foo()
print 'C.foo()'
A.foo(self)
B.foo(self)
c = C()
c.foo()
次に、super(C、self).foo()はA.fooを参照します
出力は
A.foo()
C.foo()
A.foo()
B.foo()
[編集:追加情報とリンクを含める]
ダンカンが上で述べたように、特にsuper()を一貫して使用すると、予測可能な結果を得ることができます。
次のテストの結果は私に役立ちました:
class FooBase(object):
def foo(self):
print 'FooBase.foo()'
class A(FooBase):
def foo(self):
print 'A.foo() before super()'
super(A, self).foo()
print 'A.foo() after super()'
class B(FooBase):
def foo(self):
print 'B.foo() before super()'
super(B, self).foo()
print 'B.foo() after super()'
class C(A, B):
def foo(self):
print 'C.foo() before super()'
super(C, self).foo()
print 'C.foo() after super()'
これは印刷されます:
>>> c = C()
>>> c.foo()
C.foo() before super()
A.foo() before super()
B.foo() before super()
FooBase.foo()
B.foo() after super()
A.foo() after super()
C.foo() after super()
class A:
def article(self):
print("Class A")
class B:
def article(self):
print("Class B")
class C(A,B):
def article(self):
print("Class C ::\n")
super().article()
x = C()
x.article()
クラスC ::
クラスA
これはsuper
で可能です
class A(object):
def foo(self):
print 'A.foo()'
class B(object):
def foo(self):
print 'B.foo()'
class C(A, B):
def foo(self):
print 'C.foo()'
super(C, self).foo() # calls A.foo()
super(A, self).foo() # calls B.foo()