Python 3.x の super() が魔法なのはなぜですか? 質問する

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

これを機能させるために、コンパイル時にいくつかのマジックが実行され、その結果、次のコード (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

おすすめ記事