161

Python 3.x では、super()引数なしで呼び出すことができます:

class A(object):
    def x(self):
         print("Hey now")

class B(A):
    def x(self):
        super().x()
>>> B().x()
Hey now

これを機能させるために、いくつかのコンパイル時の魔法が実行されます。その結果の 1 つは、次のコード ( に再バインドsuperするsuper_) が失敗することです。

super_ = super

class A(object):
    def x(self):
        print("No flipping")

class B(A):
    def x(self):
        super_().x()
>>> B().x()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in x
RuntimeError: super(): __class__ cell not found

super()コンパイラの支援なしでは実行時にスーパークラスを解決できないのはなぜですか? この振る舞い、またはその根底にある理由が不注意なプログラマーを噛む可能性がある実際の状況はありますか?

...そして、副次的な質問として、別の名前に再バインドすることで壊れる可能性のある関数、メソッドなどのPythonの例は他にありますか?

4

1 に答える 1

224

super()DRY (Don't Repeat Yourself) 原則に違反しないように、新しいマジック動作が追加されました。 PEP 3135を参照してください。super()クラスをグローバルとして参照して明示的に名前を付ける必要がある場合、それ自体で発見したのと同じ再バインドの問題が発生する傾向があります。

class Foo(Bar):
    def baz(self):
        return super(Foo, self).baz() + 42

Spam = Foo
Foo = something_else()

Spam().baz()  # liable to blow up

同じことがクラス デコレータの使用にも当てはまり、デコレータはクラス名を再バインドする新しいオブジェクトを返します。

@class_decorator_returning_new_class
class Foo(Bar):
    def baz(self):
        # Now `Foo` is a *different class*
        return super(Foo, self).baz() + 42

魔法のsuper() __class__セルは、元のクラス オブジェクトにアクセスできるようにすることで、これらの問題をうまく回避します。

PEP は Guido によって開始されました。Guido は当初super、キーワードになることを想定しており、セルを使用して現在のクラスを検索するというアイデアも彼のものでした。確かに、これをキーワードにするというアイデアは、PEP の最初のドラフトの一部でした。

しかし、実際には Guido 自身が「あまりにも魔法的」というキーワードのアイデアから離れ、代わりに現在の実装を提案しました。彼は、別の名前を使用すると問題が発生する可能性があると予想しましたsuper()

私のパッチは中間的な解決策を使用しています:__class__ という名前の変数を使用するたびに必要になると想定しています'super'。したがって、(グローバルに) に名前を変更superしてsupperを使用supperしないsuper場合、引数なしでは機能しません (ただし、それ __class__または実際のクラス オブジェクトを渡すと機能します)。という名前の無関係な変数がある場合、動作はしますsuperが、メソッドはセル変数に使用されるわずかに遅い呼び出しパスを使用します。

結局、superキーワードを使うのは違和感があり、魔法の__class__セルを提供することは許容できる妥協であると宣言したのはグイド自身でした.

私は、実装の魔法のような暗黙の動作がやや驚くべきものであることに同意しsuper()ますが、言語で最も誤って適用された関数の 1 つです。インターネットで見つかったすべての誤用super(type(self), self)またはsuper(self.__class__, self) 呼び出しを見てください。そのコードのいずれかが派生クラスから呼び出された場合、無限再帰の例外が発生します。少なくとも、引数なしの単純化された呼び出しは、そのsuper()問題を回避します。

名前が変更されたsuper_; __class__メソッドでも参照するだけで、再び機能します。super メソッドでまたは __class__名前のいずれかを参照すると、セルが作成されます。

>>> super_ = super
>>> class A(object):
...     def x(self):
...         print("No flipping")
... 
>>> class B(A):
...     def x(self):
...         __class__  # just referencing it is enough
...         super_().x()
... 
>>> B().x()
No flipping
于 2013-10-26T16:41:24.223 に答える