web-dev-qa-db-ja.com

super()とParentクラス名の違いは何ですか?

super()を使用することと、親クラス名を直接使用することには違いがありますか?例えば:

_class Parent:
    def __init__(self):
        print("In parent")
        self.__a=10

class Child(Parent):
    def __init__(self):
        super().__init__()     # using super()
        Parent.__init__(self)  # using Parent class name

c=Child()
_

super().__init__()Parent.__init__(self)には内部的に違いがありますか?

17
codingsplash

この場合ではありません。しかし、一般的に、特に多重継承を使用する場合、super()は、 メソッド解決順序(MRO)の次のオブジェクトに委任します。ドキュメント

super([type[, object-or-type]])

タイプの親または兄弟クラスにメソッド呼び出しを委任するプロキシオブジェクトを返します。これは、クラスでオーバーライドされた継承されたメソッドにアクセスする場合に役立ちます。検索順序は、タイプ自体がスキップされることを除いて、getattr()で使用される順序と同じです。

タイプの___mro___属性は、getattr()super()の両方で使用されるメソッド解決検索順序をリストします。 。属性は動的であり、継承階層が更新されるたびに変更できます。

(...)

(コピー、太字を追加)

たとえば、次のようなクラスを定義するとします( この質問、MROについて詳しく説明します ):

_class F:
    def __init__(self):
        print('F%s'%super().__init__)
        super().__init__()

class G: 
    def __init__(self):
        print('G%s'%super().__init__)
        super().__init__() 

class H: 
    def __init__(self):
        print('H%s'%super().__init__)
        super().__init__()

class E(G,H):
    def __init__(self):
        print('E%s'%super().__init__)
        super().__init__()

class D(E,F): 
    def __init__(self):
        print('D%s'%super().__init__)
        super().__init__() 

class C(E,G): 
    def __init__(self):
        print('C%s'%super().__init__)
        super().__init__()

class B(C,H): 
    def __init__(self):
        print('B%s'%super().__init__)
        super().__init__()

class A(D,B,E): 
    def __init__(self):
        print('A%s'%super().__init__)
        super().__init__()
_

Aの___mro___は次のとおりです。

_A.__mro__ == (A,D,B,C,E,G,H,F,object)
_

A()を呼び出すと、次のように出力されます。

_A<bound method D.__init__ of <__main__.A object at 0x7efefd8645c0>>
D<bound method B.__init__ of <__main__.A object at 0x7efefd8645c0>>
B<bound method C.__init__ of <__main__.A object at 0x7efefd8645c0>>
C<bound method E.__init__ of <__main__.A object at 0x7efefd8645c0>>
E<bound method G.__init__ of <__main__.A object at 0x7efefd8645c0>>
G<bound method H.__init__ of <__main__.A object at 0x7efefd8645c0>>
H<bound method F.__init__ of <__main__.A object at 0x7efefd8645c0>>
F<method-wrapper '__init__' of A object at 0x7efefd8645c0>
<__main__.A object at 0x7efefd8645c0>
_

つまり、Aのコンテキストで、を取得しようとすると___init___それ:

  • Asuper().__init__は_D.__init___です。
  • super().__init__ of D is _B.__init___;
  • Bsuper().__init__は_C.__init___です。
  • Csuper().__init__は_E.__init___です。
  • Esuper().__init__は_G.__init___です。
  • Gsuper().__init__は_H.__init___です。
  • Hsuper().__init__は_F.__init___です。そして
  • Fsuper().__init__は_object.__init___です。

したがって、super()それ自体は親に委任しないことに注意してください。たとえば、Dsuper()Bであり、BDのスーパークラスではないため、実際にはオブジェクトのタイプに依存します(クラスではありません)。

Dの場合、___mro___は次のようになります。

_D.__mro__ = (D,E,G,H,F,object)
_

ただし、Dを作成すると、次のようになります。

_D<bound method E.__init__ of <__main__.D object at 0x7efefd864630>>
E<bound method G.__init__ of <__main__.D object at 0x7efefd864630>>
G<bound method H.__init__ of <__main__.D object at 0x7efefd864630>>
H<bound method F.__init__ of <__main__.D object at 0x7efefd864630>>
F<method-wrapper '__init__' of D object at 0x7efefd864630>
_

したがって、Dのコンテキストでは、次のようになります。

  • super().__init__ of D is _E.__init___;
  • Esuper().__init__は_G.__init___です。
  • Gsuper().__init__は_H.__init___です。
  • Hsuper().__init__は_F.__init___です。そして
  • Fsuper().__init__は_object.__init___です。

したがって、ここDsuper()は、Eにつながります(___init___の場合)これはAのコンテキストでは同じではありません。

10
_super().__init__(*args, **kwargs)   
_

あなたが「自己」を渡さないことを認識してください-それは自動的に挿入されます。

super()は最初にPython 2で設計され、クラス階層内のミックスインとして、直接のスーパークラスが変更される可能性がある方法で再利用できるようにしました。

ある時点で、コードが次のようになっているとしましょう。

_class A: pass
class B(A): 
    def __init__(self, *args, **kwargs):
          ...
          # Fixed call to A
          A.__init__(self, *args, **kwargs)

class C(A):
    def __init__(self, *args, **kwargs):
          ...
          # Fixed call to A
          A.__init__(self, *args, **kwargs)

class D(C, B): 
    pass
_

この時点で、正しいOOPコードは_C.__init___を実行する必要があります。これにより、呼び出しが_B.__init___にチェーンされます。ただし、スーパークラス名がハードコードされている場合は発生しません-Aの___init___は常に次に来るでしょう。そして、Cに_B.__init___をハードコーディングすると、CBなしで機能するのを防ぎ、敗北します。多重継承の目的。

代わりにsuper()を使用すると、Pythonはクラスの___mro___属性(mro =メソッド解決順序。___mro___はそれぞれに付加された具体的な属性)を調べて、次の親クラスのメソッド検索を実行します。 Python class).-したがって、ある時点で上記のクラスDBから継承しなくなった場合、super().__init__への呼び出しはCは、自動的にAに直接再ルーティングされます。

また、Python 3では、使用を容易にするためにsuperのパラメーターなしの形式が導入されました。その前に、独自のクラスへの参照をハードコーディングする必要がありました。また、パラメータにselfを挿入します。この形式は、コンパイラ自体にハードコードされているPythonの数少ない例外の1つであり、super(または___class___)はメソッド本体内に表示されます(つまり、super呼び出しが使用するクラス自体を指す___class___変数を作成します)

3
jsbueno