web-dev-qa-db-ja.com

python同じメソッド名を持つ異なるパスからの多重継承

次のコードサンプルでは、​​superを使用できますか、それともCA.fooB.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)
26
sayap

super()は、特定のメソッドの単一のクラスタイプのみを解決するため、複数のクラスから継承していて、両方のクラスでメソッドを呼び出したい場合は、明示的に行う必要があります。 つまりA.foo(self)

10
Amber

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):があった場合、Asuper()呼び出しはAFooBaseのメソッドを呼び出し、Bのメソッドは呼び出されません。ベースがすべてのクラスに共通である場合、それはメソッド解決順序の最後になり、クラスがどのように定義されていても、ベースクラスメソッドが最後に呼び出されることを確信できます。

22
Duncan

このスレッドに貢献してくれたすべての人に感謝します。

要約する:

  • (現在)受け入れられている答え は不正確です。正しい説明は次のようになります: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).namesuper(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
_
17
RayLuo

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()が呼び出されます。

9
Manoj Govindan

以下を追加した場合:

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()

[編集:追加情報とリンクを含める]

5
pyfunc

ダンカンが上で述べたように、特に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()
2
bmzf
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

0
omar ahmed

これは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()
0
acktus