web-dev-qa-db-ja.com

Pythonスーパーメソッドと代替の呼び出し

スーパークラスのメソッドを呼び出す必要がある例はどこにでもあります:

super(SuperClass, instance).method(args)

行うことには欠点がありますか:

SuperClass.method(instance, args)
55
IanSR

次の状況を考慮してください。

_class A(object):
    def __init__(self):
        print('Running A.__init__')
        super(A,self).__init__()
class B(A):
    def __init__(self):
        print('Running B.__init__')        
        # super(B,self).__init__()
        A.__init__(self) 

class C(A):
    def __init__(self):
        print('Running C.__init__')
        super(C,self).__init__()
class D(B,C):
    def __init__(self):
        print('Running D.__init__')
        super(D,self).__init__()

foo=D()
_

したがって、クラスはいわゆる継承ダイヤモンドを形成します。

_    A
   / \
  B   C
   \ /
    D
_

コードを実行すると、

_Running D.__init__
Running B.__init__
Running A.__init__
_

Cの___init___がスキップされるため、それは悪いことです。その理由は、Bの___init___がAの___init___を直接呼び出すためです。

superの目的は、継承ダイアモンドを解決することです。コメントを外した場合

_# super(B,self).__init__()
_

およびコメントアウト

_A.__init__(self) 
_

コードはより望ましい結果をもたらします:

_Running D.__init__
Running B.__init__
Running C.__init__
Running A.__init__
_

これで、すべての___init___メソッドが呼び出されます。 _B.__init___を定義する時点で、super(B,self).__init__()A.__init__(self)の呼び出しと同じthinkかもしれませんが、間違っているだろう。上記の状況では、super(B,self).__init__()は実際にC.__init__(self)を呼び出します。

聖なる煙、BCについて何も知らないが、super(B,self)Cの___init___を呼び出すことを知っているself.__class__.mro()にはCが含まれているためです。言い換えれば、self(または上記では、foo)はCを知っています。

そのため、注意してください-この2つは代用できません。彼らは非常に異なる結果をもたらすことができます。

superの使用 落とし穴があります。 継承図のすべてのクラス間でかなりのレベルの調整が必要です。 (たとえば、特定の___init___は、他の___init___ superが次に呼び出すかもしれないことを知らないため、___init___に対して同じ呼び出しシグネチャを持つ必要があります。 _**kwargs_ を使用します。さらに、superをどこでも使用することについて一貫している必要があります。 (上記の例のように)一度スキップすると、superの目的全体が無効になります。その他の落とし穴については、リンクを参照してください。

クラス階層を完全に制御できる場合、または継承ダイアモンドを回避する場合は、superは不要です。

105
unutbu

あなたの例は多少見当違いですが、ペナルティはありません。最初の例では、

super(SubClass, instance).method(args)  # Sub, not SuperClass

そして、それは Pythonドキュメント を引用することにつながります:

superには2つの典型的な使用例があります。単一継承のクラス階層では、superを使用して、明示的に名前を付けずに親クラスを参照できるため、コードの保守性が向上します。これは、他のプログラミング言語でのsuperの使用と密接に類似しています。

2番目の使用例は、動的な実行環境で協調的な多重継承をサポートすることです。この使用例はPythonに固有であり、静的にコンパイルされた言語や単一の継承のみをサポートする言語には見られません。これにより、複数の基本クラスが同じメソッドを実装する「ダイヤモンドダイアグラム」を実装できます良い設計では、このメソッドはすべてのケースで同じ呼び出しシグネチャを持つことが規定されています(呼び出しの順序は実行時に決定されます。なぜなら、その順序はクラス階層の変更に適応し、その順序には前に未知の兄弟クラスを含めることができるためです)ランタイム)。

基本的に、最初のメソッドを使用することで、単一クラス階層のために親クラスをそこにハードコーディングする必要がなく、複数を使用するときに2番目のメソッドを使用して(効率的/効果的に)本当にあなたが望むことを実際に行うことはできません継承。

8
Daniel DiPaolo