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)
には内部的に違いがありますか?
この場合ではありません。しかし、一般的に、特に多重継承を使用する場合、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__
_それ:
A
のsuper().__init__
は_D.__init__
_です。super().__init__
of D
is _B.__init__
_;B
のsuper().__init__
は_C.__init__
_です。C
のsuper().__init__
は_E.__init__
_です。E
のsuper().__init__
は_G.__init__
_です。G
のsuper().__init__
は_H.__init__
_です。H
のsuper().__init__
は_F.__init__
_です。そしてF
のsuper().__init__
は_object.__init__
_です。したがって、super()
はそれ自体は親に委任しないことに注意してください。たとえば、D
のsuper()
はB
であり、B
はD
のスーパークラスではないため、実際にはオブジェクトのタイプに依存します(クラスではありません)。
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__
_;E
のsuper().__init__
は_G.__init__
_です。G
のsuper().__init__
は_H.__init__
_です。H
のsuper().__init__
は_F.__init__
_です。そしてF
のsuper().__init__
は_object.__init__
_です。したがって、ここD
のsuper()
は、E
につながります(___init__
_の場合)これはA
のコンテキストでは同じではありません。
_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__
_をハードコーディングすると、C
がB
なしで機能するのを防ぎ、敗北します。多重継承の目的。
代わりにsuper()
を使用すると、Pythonはクラスの___mro__
_属性(mro =メソッド解決順序。___mro__
_はそれぞれに付加された具体的な属性)を調べて、次の親クラスのメソッド検索を実行します。 Python class).-したがって、ある時点で上記のクラスD
がB
から継承しなくなった場合、super().__init__
への呼び出しはC
は、自動的にA
に直接再ルーティングされます。
また、Python 3では、使用を容易にするためにsuper
のパラメーターなしの形式が導入されました。その前に、独自のクラスへの参照をハードコーディングする必要がありました。また、パラメータにself
を挿入します。この形式は、コンパイラ自体にハードコードされているPythonの数少ない例外の1つであり、super
(または___class__
_)はメソッド本体内に表示されます(つまり、super
呼び出しが使用するクラス自体を指す___class__
_変数を作成します)