シュレーディンガーの変数: __class__ セルの存在を確認すると、魔法のように表示されますか? 質問する

シュレーディンガーの変数: __class__ セルの存在を確認すると、魔法のように表示されますか? 質問する

ここに驚きがあります:

>>> class B:
...     print(locals())
...     def foo(self):
...         print(locals())
...         print(__class__ in locals().values())
...         
{'__module__': '__main__', '__qualname__': 'B'}
>>> B().foo()
{'__class__': <class '__main__.B'>, 'self': <__main__.B object at 0x7fffe916b4a8>}
True

の単なる言及は__class__パーサーによって明示的にチェックされているようです。そうでなければ、次のようなものになるはずです。

NameError: name '__class__' is not defined

実際、キーのみをチェックするように変更すると、つまり をチェックすると、予想どおりに のスコープ'__class__' in locals()のみになります。self

この変数がスコープに魔法のように挿入されるのはどうしてでしょうか? 私の推測では、これは - と関係があると思いますsuperが、 を使用しなかったのでsuper、必要がないのにコンパイラがここで暗黙のクロージャ参照を作成するのはなぜでしょうか?

ベストアンサー1

これは、Python 3 の引数なしの の実装における奇妙な相互作用です。メソッド内でsuperにアクセスすると、メソッドを定義するクラスを参照する非表示のクロージャ変数の追加がトリガーされます。パーサーは、メソッドのシンボル テーブルにも を追加することで、メソッド内の名前の読み込みを特別扱いし、その後、関連するコードの残りはすべて の代わりにを探します。ただし、自分で にアクセスしようとすると、 を探しているすべてのコードがそれを認識し、処理を実行する必要があると考えます。super__class__super__class____class__super__class____class__super

__class__ここで、シンボルテーブルに名前が追加されます。super:

case Name_kind:
    if (!symtable_add_def(st, e->v.Name.id,
                          e->v.Name.ctx == Load ? USE : DEF_LOCAL))
        VISIT_QUIT(st, 0);
    /* Special-case super: it counts as a use of __class__ */
    if (e->v.Name.ctx == Load &&
        st->st_cur->ste_type == FunctionBlock &&
        !PyUnicode_CompareWithASCIIString(e->v.Name.id, "super")) {
        if (!GET_IDENTIFIER(__class__) ||
            !symtable_add_def(st, __class__, USE))
            VISIT_QUIT(st, 0);
    }
    break;

こちらはdrop_class_freeは次のように設定しますste_needs_class_closure:

static int
drop_class_free(PySTEntryObject *ste, PyObject *free)
{
    int res;
    if (!GET_IDENTIFIER(__class__))
        return 0;
    res = PySet_Discard(free, __class__);
    if (res < 0)
        return 0;
    if (res)
        ste->ste_needs_class_closure = 1;
    return 1;
}

コンパイラセクションste_needs_class_closure暗黙のセルを確認して作成します。

if (u->u_ste->ste_needs_class_closure) {
    /* Cook up an implicit __class__ cell. */
    _Py_IDENTIFIER(__class__);
    PyObject *tuple, *name, *zero;
    int res;
    assert(u->u_scope_type == COMPILER_SCOPE_CLASS);
    assert(PyDict_Size(u->u_cellvars) == 0);
    name = _PyUnicode_FromId(&PyId___class__);
    if (!name) {
        compiler_unit_free(u);
        return 0;
    }
    ...

関連するコードは他にもありますが、すべてを記載するのは多すぎます。Python/compile.cそしてPython/symtable.cさらに詳しく知りたい場合はこちらをご覧ください。

次のような変数を使用しようとすると、奇妙なバグが発生する可能性があります__class__

class Foo:
    def f(self):
        __class__ = 3
        super()

Foo().f()

出力:

Traceback (most recent call last):
  File "./prog.py", line 6, in <module>
  File "./prog.py", line 4, in f
RuntimeError: super(): __class__ cell not found

__class__meansへの割り当ては__class__クロージャ変数ではなくローカル変数なので、クロージャ セルはsuper()必要ありません。

def f():
    __class__ = 2
    class Foo:
        def f(self):
            print(__class__)

    Foo().f()

f()

出力:

<class '__main__.f.<locals>.Foo'>

囲むスコープ内に実際の変数が存在する場合でも__class__、 の特殊ケース__class__により、囲むスコープの変数値ではなくクラスが取得されます。

おすすめ記事