ここに驚きがあります:
>>> 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__
により、囲むスコープの変数値ではなくクラスが取得されます。