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
これを機能させるために、コンパイル時にいくつかのマジックが実行され、その結果、次のコード (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 には、関数やメソッドなどを別の名前に再バインドすることで壊れる例が他にもありますか?
ベストアンサー1
新しいマジックsuper()
動作は、DRY(Don't Repeat Yourself)原則に違反しないようにするために追加されました。ペップ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の最初の草案。
しかし、実際にはグイド自身がキーワードのアイデアは「魔法的すぎる」としてやめました代わりに現在の実装を提案した。彼は別の名前を使用するとsuper()
問題が発生する可能性があると予想した:
私のパッチは中間的な解決策を使用します。
__class__
という名前の変数を使用するときは常にが必要であると想定します'super'
。したがって、 を (グローバルに)super
に名前変更してsupper
を使用し、 を使用supper
しない場合super
、引数なしでは機能しません (ただし、 または__class__
実際のクラス オブジェクトを渡すと機能します)。 という名前の無関係な変数がある場合super
、すべては機能しますが、メソッドはセル変数に使用されるわずかに遅い呼び出しパスを使用します。
つまり、最終的には、super
キーワードを使用するのは適切ではないと感じ、魔法の__class__
セルを提供することは許容できる妥協案であると主張したのは Guido 自身でした。
実装の魔法のような暗黙の動作は多少驚くべきものであることに同意しますが、これは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