web-dev-qa-db-ja.com

Python 3.xのsuper()マジックはなぜですか?

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つは、次のコード(supersuper_に再バインド)が失敗することです。

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の関数、メソッドなどの別の例はありますか?

150
Zero Piraeus

D.R.Yへの違反を回避するために、新しいマジックsuper()動作が追加されました。 (自分自身を繰り返さないでください)原則、 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は 最初はsuperがキーワードになることを想定していた と、現在のクラスを検索するためにセルを使用するというアイデア も彼の によって開始されました。確かに、キーワードにするというアイデアは PEPの最初のドラフト の一部でした。

ただし、実際には 'too magical'としてキーワードのアイデアから離れました が実際にGuido自身であり、代わりに現在の実装を提案しました。彼は super()に別の名前を使用すると問題になる可能性があると予想していました

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

そのため、最終的には、superキーワードを使用することは正しくないと感じ、マジック__class__セルを提供することは許容できる妥協であると宣言したのはGuido自身でした。

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

名前が変更されたsuper_;メソッド内の__class__を参照するだけで、再び機能します。メソッドでsuperor__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
209
Martijn Pieters