私はsuper()
の使い方を理解しようとしています。その外観から、両方の子クラスを作成できます。
次の2つの子クラスの実際の違いについて知りたいのですが。
class Base(object):
def __init__(self):
print "Base created"
class ChildA(Base):
def __init__(self):
Base.__init__(self)
class ChildB(Base):
def __init__(self):
super(ChildB, self).__init__()
ChildA()
ChildB()
super()
を使うと、基底クラスを明示的に参照しなくて済みます。しかし、主な利点は、あらゆる種類の 楽しいもの が発生する可能性がある多重継承にあります。まだお持ちでない場合は、 superに関する標準文書 を参照してください。
Python 3.0で構文が変更されました :IMOのほうがかなり良いsuper().__init__()
の代わりにsuper(ChildB, self).__init__()
と言うことができます。標準的なドキュメントでは super()を使用するためのガイド も参照していますが、これは非常に説明的なものです。
私は
super()
を理解しようとしています
super
を使用する理由は、協調多重継承を使用している可能性がある子クラスが、メソッド解決順序(MRO)で正しい次の親クラス関数を呼び出すようにするためです。
Python 3では、このように呼ぶことができます。
class ChildB(Base):
def __init__(self):
super().__init__()
Python 2では、このように使う必要があります。
super(ChildB, self).__init__()
Superがないと、多重継承を使うことができません。
Base.__init__(self) # Avoid this.
以下でさらに説明します。
「このコードには実際にどのような違いがありますか?」
class ChildA(Base):
def __init__(self):
Base.__init__(self)
class ChildB(Base):
def __init__(self):
super(ChildB, self).__init__()
# super().__init__() # you can call super like this in Python 3!
このコードの主な違いは、super
を使用して__init__
で間接層を取得することです。このクラスでは、現在のクラスを使用してMROで検索する次のクラスの__init__
を決定します。
私は 標準的な質問、Pythonで 'super'を使うには? で、 依存性注入 と 協調的多重継承 を示しています。)で答えを説明します。
super
を持っていなかった場合これは実際にsuper
と非常によく似たコードです(Cでの実装方法から、チェックやフォールバックの動作を除いたもの、そしてPythonに翻訳されたもの)。
class ChildB(Base):
def __init__(self):
mro = type(self).mro() # Get the Method Resolution Order.
check_next = mro.index(ChildB) + 1 # Start looking after *this* class.
while check_next < len(mro):
next_class = mro[check_next]
if '__init__' in next_class.__dict__:
next_class.__init__(self)
break
check_next += 1
もう少しネイティブPythonのように書いた:
class ChildB(Base):
def __init__(self):
mro = type(self).mro()
for next_class in mro[mro.index(ChildB) + 1:]: # slice to end
if hasattr(next_class, '__init__'):
next_class.__init__(self)
break
super
オブジェクトがない場合は、メソッド解決順序で適切な次のメソッドを呼び出すように、このマニュアルコードをいたるところに書く(または再作成する必要があります)必要があります。
それが呼び出されたメソッドからどのクラスとインスタンスを明示的に言われずにPython 3ではどうやってこれをスーパーにしますか?
呼び出し元のスタックフレームを取得し、クラス(ローカルの自由変数__class__
として暗黙的に格納され、呼び出し側関数をクラスのクロージャにする)と、その関数への最初の引数(インスタンスまたはクラスであることを通知する)を見つけます。どのメソッド解決順序(MRO)を使用するか。
それはMROのための最初の引数を必要とするので、 静的メソッドでsuper
を使うことは不可能です 。
super()を使用すると、基底クラスを明示的に参照しなくて済みます。 。しかし主な利点は、あらゆる種類の楽しいものが発生する可能性がある多重継承にあります。まだ持っていなければ、superの標準文書を見てください。
それはどちらかといえば手の込んだもので、あまり説明しませんが、super
のポイントは、親クラスを書かないようにすることではありません。ポイントは、メソッド解決順序(MRO)内の次のメソッドが確実に呼び出されるようにすることです。これは多重継承において重要になります。
ここで説明します。
class Base(object):
def __init__(self):
print("Base init'ed")
class ChildA(Base):
def __init__(self):
print("ChildA init'ed")
Base.__init__(self)
class ChildB(Base):
def __init__(self):
print("ChildB init'ed")
super(ChildB, self).__init__()
それでは、子の後に呼び出される依存関係を作成しましょう。
class UserDependency(Base):
def __init__(self):
print("UserDependency init'ed")
super(UserDependency, self).__init__()
ChildB
はsuperを使用していますが、ChildA
は使用していません。
class UserA(ChildA, UserDependency):
def __init__(self):
print("UserA init'ed")
super(UserA, self).__init__()
class UserB(ChildB, UserDependency):
def __init__(self):
print("UserB init'ed")
super(UserB, self).__init__()
そしてUserA
はUserDependencyメソッドを呼び出しません。
>>> UserA()
UserA init'ed
ChildA init'ed
Base init'ed
<__main__.UserA object at 0x0000000003403BA8>
UserB
はChildB
を使っているので、super
はそうします:
>>> UserB()
UserB init'ed
ChildB init'ed
UserDependency init'ed
Base init'ed
<__main__.UserB object at 0x0000000003403438>
ChildBをサブクラス化すると間違いなくエラーになるので、決して絶対にしないでください。
super(self.__class__, self).__init__() # Don't do this. Ever.
(その答えは巧妙でも特に興味深いものでもありませんが、コメントに対する直接的な批判と17回以上の投票にもかかわらず、回答者は親切な編集者が問題を解決するまでそれを提案し続けました。)
説明:その答えは次のようにsuperを呼ぶことを提案しました:
super(self.__class__, self).__init__()
これは完全に間違っています。 super
を使用すると、子クラスについてMRO内の次の親を検索できます(この回答の最初のセクションを参照)。 super
を子インスタンスのメソッドの中に入れると、次のメソッドをインラインで検索し(おそらくこれ)、再帰が発生し、おそらく論理的な失敗(応答者の例ではそうなる)、または次の場合はRuntimeError
が発生します。再帰の深さを超えています。
>>> class Polygon(object):
... def __init__(self, id):
... self.id = id
...
>>> class Rectangle(Polygon):
... def __init__(self, id, width, height):
... super(self.__class__, self).__init__(id)
... self.shape = (width, height)
...
>>> class Square(Rectangle):
... pass
...
>>> Square('a', 10, 10)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in __init__
TypeError: __init__() missing 2 required positional arguments: 'width' and 'height'
Python 3.0以降ではあなたが使えることが注目されています
super().__init__()
これは簡潔で、親のORクラス名を明示的に参照する必要がないため便利です。 Python 2.7以下の場合は、クラス名の代わりにself.__class__
を書くことで、この名前に依存しない動作を実現することが可能です。
super(self.__class__, self).__init__()
しかし、これはあなたのクラスから継承するクラスに対するsuper
への呼び出しを中断します。ここでself.__class__
は子クラスを返すことができます。例えば:
class Polygon(object):
def __init__(self, id):
self.id = id
class Rectangle(Polygon):
def __init__(self, id, width, height):
super(self.__class__, self).__init__(id)
self.shape = (width, height)
class Square(Rectangle):
pass
ここにクラスSquare
があります。これはRectangle
のサブクラスです。 Square
のコンストラクタは十分であるため、Rectangle
のための別のコンストラクタを書きたくないとしましょう。
Square
を独自のコンストラクタに与えていないため、mSquare = Square('a', 10,10)
を使用してRectangle
を作成するとき、PythonはSquare
のコンストラクタを呼び出します。しかし、Rectangle
のコンストラクターでは、呼び出しsuper(self.__class__,self)
はmSquare
のスーパークラスを戻すことになるので、再度Rectangle
のコンストラクターを呼び出します。 @S_Cで述べたように、これが無限ループの発生方法です。この場合、super(...).__init__()
を実行するとRectangle
のコンストラクタを呼び出していますが、引数を与えないのでエラーになります。
スーパーは副作用がありません
Base = ChildB
Base()
期待通りに動作
Base = ChildA
Base()
無限再帰に入ります。
本当にありません。 super()
はMRO内の次のクラス(メソッド解決順、cls.__mro__
でアクセス)を見てメソッドを呼び出します。ベース__init__
を呼び出すだけで、ベース__init__
が呼び出されます。それが起こるように、MROはちょうど1つのアイテム - ベースを持っています。それで、あなたは本当に同じことをしていますが、super()
を使ってより良い方法でしています(特に、後で多重継承をする場合)。