薛定谔变量:如果您正在检查 __class__ 单元格是否存在,它会神奇地出现吗?

Schrödinger's variable: the __class__ cell magically appears if you're checking for its presence?

这里有惊喜:

>>> 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,那么如果不需要,为什么编译器会在此处创建隐式闭包引用?

这是 Python 3 的无参数 super 实现中的一个奇怪交互。在方法中访问 super 会触发添加一个隐藏的 __class__ 闭包变量,该变量引用定义该方法的 class。解析器通过将 __class__ 添加到方法的符号 table 来在方法中特殊加载名称 super,然后其余相关代码全部查找 __class__ 而不是 super。但是,如果您尝试自己访问 __class__,所有寻找 __class__ 的代码都会看到它并认为它应该执行 super 处理!

Here's where it adds the name __class__ to the symbol table if it sees 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并创建隐式单元格的compiler section

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 and 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__ 的赋值意味着 __class__ 是局部变量而不是闭包变量,因此闭包单元 super() 不存在。

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

    Foo().f()

f()

输出:

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

即使在封闭范围内有一个实际的 __class__ 变量,__class__ 的特殊大小写意味着您得到 class 而不是封闭范围的变量值。

https://docs.python.org/3/reference/datamodel.html#creating-the-class-object

__class__ is an implicit closure reference created by the compiler if any methods in a class body refer to either __class__ or super. This allows the zero argument form of super() to correctly identify the class being defined based on lexical scoping, while the class or instance that was used to make the current call is identified based on the first argument passed to the method.