1

ここで python の super() メソッドに関する他の質問を見てきましたが、概念全体を理解するのはまだ難しいと感じています。

私は本pro pythonの例も見ています

そこで参考にした例は

class A(object):
     def test(self):
         return 'A'

class B(A):
     def test(self):
         return 'B-->' + super(B, self).test()

class C(A):
     def test(self):
         return 'C'

class D(B, C):
      pass

>>> A().test()
'A'
>>> B().test()
'B-->A'
>>> C().test()
'C'
>>> D().test()
'B-->C'

>>> A.__mro__
(__main__.A, object)

>>> B.__mro__
(__main__.B, __main__.A, object)

>>> C.__mro__
(__main__.C, __main__.A, object)

>>> D.__mro__
(__main__.D, __main__.B, __main__.C, __main__.A, object)

D().test() を実行すると、出力が「B-->A」ではなく「B-->C」となる理由

本の解説は、

ここに示した使用法を含む最も一般的なケースでは、super() はクラスとそのクラスのインスタンスの 2 つの引数を取ります。ここでの例が示すように、インスタンス オブジェクトは、結果のオブジェクトの属性を解決するために使用される MRO を決定します。super() は、提供されたクラスの後に発生する MRO 内のエントリのみを使用するため、提供されたクラスはその MRO のサブセットを決定します。

今でも説明がわかりにくいと思います。これは重複の可能性があり、これと同様の質問が何度も出されていますが、これを理解すれば、他の質問の残りをよりよく理解できるかもしれません.

__init__() メソッドを使用した Python の super() を理解する

Pythonで「スーパー」は何をしますか?

Python、継承、super() メソッド

[python]: super() で混乱

4

3 に答える 3

5

Python がこの特定の MRO アルゴリズムを選択した理由を知りたい場合は、メーリング リストのアーカイブに議論があり、 The Python 2.3 Method Resolution Orderに簡単に要約されています。

しかし、実際には、次のようになります。Python 2.2 のメソッド解決は、多重継承を処理するときに壊れていました。それを修正するために最初に提案されたのは、Dylan から C3 アルゴリズムを借りることでした。そのため、Python は C3 を使用します。


他のアルゴリズムに対する C3 の一般的な利点 (および欠点) に関心がある場合は…

BrenBarn と florquake の回答は、この質問の基本を示しています。Python の super() はスーパーと見なされます! レイモンド ヘッティンガーのブログからは、同じ趣旨のより長く、より詳細な議論があり、間違いなく読む価値があります。

A Monotonic Superclass Linearlization for Dylanは、設計を説明する元の論文です。もちろん、Dylan は Python とは非常に異なる言語であり、これは学術論文ですが、理論的根拠は依然として非常に優れています。

最後に、The Python 2.3 Method Resolution Order (上記でリンクされた同じドキュメント) には、利点に関するいくつかの議論があります。

さらに先に進むには、代替案について、またそれらがどのように Python に適しているか、または適切でないかについて、多くを学ぶ必要があります。または、SO についてさらに詳しい情報が必要な場合は、より具体的な質問をする必要があります。


最後に、「方法」の質問をしている場合:

を呼び出すとD().test()、明らかにBtestメソッドで定義したコードが呼び出されます。そしてB.__mro__です(__main__.B, __main__.A, object)。では、どうすれば の代わりにのメソッドをsuper(B, self).test()呼び出すことができるでしょうか?CtestA

ここで重要なのは、MRO が の型に基づいており、メソッドが定義さselfれた型に基づいていないことです。関数の内部に入ると、 ではなくであることがわかります。Btestprint(type(self))testDB

したがって、super(B, self)実際にself.__class__.__mro__(この場合はD.__mro__) を取得し、リスト内で検索Bして、その次のものを返します。かなり簡単です。

しかし、それは MRO がどのように機能するかを説明するものではなく、MRO が何をするかを説明するだけです。からメソッドをD().test()呼び出すにはどうすればよいですか?BselfD

まず、 と は同じ関数ではないことに注意しD().testD.testくださいB.test。これらはまったく関数ではないためです。それらはメソッドです。(ここでは Python 2.x を想定しています。3.x では少し異なり、主に単純です。)

im_funcメソッドは、基本的に、、、im_classおよびim_selfメンバを持つオブジェクトです。メソッドを呼び出すときは、最初に追加の引数として詰め込まれた(そうでない場合)im_funcを使用して、その を呼び出すだけです。im_selfNone

したがって、3 つの例はすべて同じ を持ちます。im_funcこれは、実際に内部で定義した関数ですB。しかし、最初の 2 つはforではDなく、最初のインスタンスもforの代わりにインスタンスを持っています。そのため、それを呼び出すと、インスタンスが として渡されます。Bim_classDNoneim_selfDself

それで、どのようにしてそれと にD().testなりim_selfますim_classか?それはどこで作成されますか?それが楽しい部分です。完全な説明については、Descriptor HowTo Guideをお読みください。

を記述するたびにfoo.bar、実際に起こることは、次のgetattr(foo, 'bar')ようなことを行う への呼び出しと同等です (インスタンス属性、__getattr____getattribute__、スロット、ビルトインなどを無視します)。

def getattr(obj, name):
    for cls in obj.__class__.__mro__:
        try:
            desc = cls.__dict__[name]
        except KeyError:
            pass
        else:
            return desc.get(obj.__class__, obj)

最後にそれ.get()が魔法のビットです。関数 (たとえば )を見ると、実際にメソッドB.test.im_funcがあることがわかります。そしてそれが行うことは、それ自体、クラス、およびオブジェクトとしてgetバインドされたメソッドを作成することです。im_funcim_classobj.__class__im_selfobj

于 2013-09-11T01:51:26.457 に答える
4

簡単に言えば、メソッド解決順序はおおよそ「幅優先」です。つまり、スーパークラスに移動する前に、特定の祖先レベルのすべての基本クラスを通過します。したがって、D が B および C から継承し、どちらも A から継承する場合、MRO は常に A の前に BおよびCを持ちます。

別の考え方としては、順序が B->A の場合、が のサブクラスであるにもかかわらず、が のA.test前に呼び出されるということです。通常、サブクラスの実装は、スーパークラスの実装よりも前に呼び出される必要があります (サブクラスの実装は、スーパークラスを完全にオーバーライドし、まったく呼び出さないようにしたい場合があるため)。C.testCA

より長い説明はここにあります。Stackoverflow で「Python メソッド解決順序」または「Python MRO」に関する質問をグーグルまたは検索することで、有用な情報を見つけることもできます。

于 2013-09-11T00:39:54.180 に答える