4

には、多数のポップアップが表示されるこのsuper()素晴らしいブログ投稿や、Stack Overflow に関する多くの質問など、多くの優れたリソースがあります。ただし、最も一般的なケース (任意の継承グラフを使用) でどのように機能するか、および内部で何が行われているのかを説明するには不十分なように感じます。

次のダイヤモンド継承の基本的な例を考えてみましょう。

class A(object):
    def foo(self):
        print 'A foo'

class B(A):
    def foo(self):
        print 'B foo before'
        super(B, self).foo()
        print 'B foo after'

class C(A):
    def foo(self):
        print 'C foo before'
        super(C, self).foo()
        print 'C foo after'

class D(B, C):
    def foo(self):
        print 'D foo before'
        super(D, self).foo()
        print 'D foo after'

このようなソースからメソッド解決順序に関する Python のルールを読んだり、C3 線形化のウィキペディア ページを調べたりすると、MRO が(D, B, C, A, object). これはもちろん次のように確認されていD.__mro__ます。

(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <type 'object'>)

d = D()
d.foo()

版画

D foo before
B foo before
C foo before
A foo
C foo after
B foo after
D foo after

これは MRO に一致します。ただし、上記のsuper(B, self).foo()in ではB実際に が呼び出されるC.fooのに対し、 inb = B(); b.foo()では単に に直行することを考慮してA.fooください。明らかに、使用することは、時々教えられるようにsuper(B, self).foo()単純な近道ではありません。A.foo(self)

super()は、その前の以前の呼び出しと、チェーンが従おうとしている全体的な MRO を明らかに認識しています。これを達成する方法は 2 つあります。super1 つ目は、チェーン内の次のメソッドにオブジェクト自体を引数として渡すようなことですself。このメソッドは、元のオブジェクトのように動作しますが、この情報も含みます。ただし、これは多くのことを壊すようにも思われますが (これsuper(D, d) is dは誤りです)、少し実験することで、そうではないことがわかります。

もう 1 つのオプションは、MRO とその中の現在の位置を格納するある種のグローバル コンテキストを持つことです。のアルゴリズムsuperは次のようになると思います。

  1. 現在、私たちが取り組んでいる状況はありますか? そうでない場合は、キューを含むものを作成します。クラス引数の MRO を取得し、最初の要素を除くすべての要素をキューにプッシュします。
  2. 現在のコンテキストの MRO キューから次の要素をポップし、superインスタンスの構築時にそれを現在のクラスとして使用します。
  3. インスタンスからメソッドにアクセスするときはsuper、現在のクラスでそれを検索し、同じコンテキストを使用して呼び出します。

ただし、これは、 への呼び出しの最初の引数として別の基本クラスを使用したり、別のメソッドを呼び出したりするなどの奇妙なことを考慮していませんsuper。これの一般的なアルゴリズムを知りたいです。また、このコンテキストがどこかに存在する場合、それを調べることはできますか? いじってもいいですか?もちろんひどい考えですが、Python は通常、あなたが成熟した大人でなくても、成熟した大人であることを期待しています。

これにより、多くの設計上の考慮事項も導入されます。Bとの関係だけを考えて書いた場合A、後で誰かが書きC、第三者が書いたD場合、私のメソッドは、私が書いた時点では存在しませんでしたが、互換性のある方法でB.foo()呼び出さなければなりません! クラスを簡単に拡張できるようにする場合は、これを説明する必要がありますが、すべてのバージョンの署名が同一であることを確認するよりも複雑かどうかはわかりません。の基本クラスのみを考慮して違いがないとしても、 の呼び出しの前後にいつコードを配置するかという問題もあります。superC.foo()foosuperB

4

1 に答える 1