web-dev-qa-db-ja.com

__init __()メソッドを使ったPython super()の理解

私は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()
2238
Mizipzor

super()を使うと、基底クラスを明示的に参照しなくて済みます。しかし、主な利点は、あらゆる種類の 楽しいもの が発生する可能性がある多重継承にあります。まだお持ちでない場合は、 superに関する標準文書 を参照してください。

Python 3.0で構文が変更されました :IMOのほうがかなり良いsuper().__init__()の代わりにsuper(ChildB, self).__init__()と言うことができます。標準的なドキュメントでは super()を使用するためのガイド も参照していますが、これは非常に説明的なものです。

1606
Kiv

私は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'を使うには? で、 依存性注入 協調的多重継承 を示しています。)で答えを説明します。

Pythonが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>

UserBChildBを使っているので、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'
568
Aaron Hall

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のコンストラクタを呼び出していますが、引数を与えないのでエラーになります。

239
AnjoMan

スーパーは副作用がありません

Base = ChildB

Base()

期待通りに動作

Base = ChildA

Base()

無限再帰に入ります。

79
S C

Python 2.7では、頭を上げて... super()がバージョン2.2で導入されて以来ずっと、あなたは super() を呼び出すことができるのは、親の1人が最終的にobject_(スタイルクラス )。

個人的には、python 2.7コードに関しては、実際にBaseClassName.__init__(self, args)を使用する利点が得られるまでsuper()を使用します。

71
rgenito

本当にありません。 super()はMRO内の次のクラス(メソッド解決順、cls.__mro__でアクセス)を見てメソッドを呼び出します。ベース__init__を呼び出すだけで、ベース__init__が呼び出されます。それが起こるように、MROはちょうど1つのアイテム - ベースを持っています。それで、あなたは本当に同じことをしていますが、super()を使ってより良い方法でしています(特に、後で多重継承をする場合)。

50