1. ホーム
  2. python

[解決済み] クラスメソッドでsuperを使用する

2023-03-14 07:55:25

質問

Pythonのsuper()関数を勉強しています。

この例(2.6)を見るまで、私はそれを把握していると思っていましたが、私自身が行き詰っていることに気づきました。

http://www.cafepy.com/article/python_attributes_and_methods/python_attributes_and_methods.html#super-with-classmethod-example

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "test.py", line 9, in do_something
    do_something = classmethod(do_something)
TypeError: unbound method do_something() must be called with B instance as first argument (got nothing instead)
>>>

例の直前のこの行を読んで、予想と違っていました。

クラスメソッドを使用している場合、superを呼び出すためのインスタンスを持っていないのです。幸いなことに、superは第2引数に型を与えても動作します。---以下のように、型は直接superに渡すことができます。

これはまさにPythonがdo_something()はBのインスタンスで呼び出されるべきであると言って、不可能だと教えてくれているものです。

どのように解決するのですか?

テキストは、詳細よりもむしろアイデアの味を楽しむために読まなければならないことがあります。これはそのような場合の一つです。

では リンク先ページ で、例2.5、2.6、2.7はすべて1つのメソッドを使用する必要があります。 do_your_stuff . (つまり do_something は次のように変更されます。 do_your_stuff .)

さらに Ned Deily が指摘したように , A.do_your_stuff はクラスメソッドでなければなりません。

class A(object):
    @classmethod
    def do_your_stuff(cls):
        print 'This is A'

class B(A):
    @classmethod
    def do_your_stuff(cls):
        super(B, cls).do_your_stuff()

B.do_your_stuff()

super(B, cls).do_your_stuff バウンド メソッド( 脚注2 ). このため cls の第二引数として渡された super() であれば、それは cls であり、返されたメソッドに束縛されます。言い換えれば cls はメソッドの最初の引数として渡されます。 do_your_stuff() の第一引数として渡されます。

再掲すると super(B, cls).do_your_stuff() 原因 A 's do_your_stuff メソッドを で呼び出される cls が第一引数として渡されます。それが動作するためには A 's do_your_stuff はクラスメソッドでなければなりません。リンク先のページではそのことに触れていない。 が、これは決定的にそうです。

PS. do_something = classmethod(do_something) は、classmethod を作る古い方法です。 新しい(er)方法は、@classmethodデコレータを使うことです。


注意点として super(B, cls) で置き換えることはできません。 super(cls, cls) . そうすると、無限ループになる可能性があります。例えば

class A(object):
    @classmethod
    def do_your_stuff(cls):
        print('This is A')

class B(A):
    @classmethod
    def do_your_stuff(cls):
        print('This is B')
        # super(B, cls).do_your_stuff()  # CORRECT
        super(cls, cls).do_your_stuff()  # WRONG

class C(B):
    @classmethod
    def do_your_stuff(cls):
        print('This is C')
        # super(C, cls).do_your_stuff()  # CORRECT
        super(cls, cls).do_your_stuff()  # WRONG

C.do_your_stuff()

RuntimeError: maximum recursion depth exceeded while calling a Python object .

もし clsC であれば super(cls, cls) は検索する C.mro() の後にあるクラスを検索します。 C .

In [161]: C.mro()
Out[161]: [__main__.C, __main__.B, __main__.A, object]

そのクラスは B であるとき clsC , super(cls, cls).do_your_stuff() 常に コール B.do_your_stuff . 以来 super(cls, cls).do_your_stuff() の内部で呼び出されます。 B.do_your_stuff の中で呼び出されると、最終的に B.do_your_stuff を無限ループで呼び出すことになります。

Python3では の0-引数形式を super が追加されたので super(B, cls) で置き換えることができます。 super() と置き換えることができ、Python3は文脈から super() の定義にある class B と等価であるべきです。 super(B, cls) .

しかし、どのような状況でも super(cls, cls) (または同様の理由で super(type(self), self) ) は決して正しくありません。