薛定谔的变量:如果你正在检查它的存在,那么__class__单元格会神奇地出现?

wim*_*wim 22 python closures python-datamodel python-3.x

这里有一个惊喜:

>>> 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
Run Code Online (Sandbox Code Playgroud)

似乎只是提到__class__了解析器明确检查了吗?否则我们应该得到类似的东西

NameError: name '__class__' is not defined
Run Code Online (Sandbox Code Playgroud)

实际上,如果您修改为仅检查密钥,即检查'__class__' in locals(),那么我们只self在预期范围内.

如何将这个变量神奇地注入范围?我的猜测是与此有关super- 但我没有使用super,那么为什么编译器在不需要时会在这里创建一个隐式闭包引用呢?

use*_*ica 20

这是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;
Run Code Online (Sandbox Code Playgroud)

这是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;
}
Run Code Online (Sandbox Code Playgroud)

检查并创建隐式单元格的编译器部分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;
    }
    ...
Run Code Online (Sandbox Code Playgroud)

有更多相关代码,但包含所有代码太多了.如果你想看到更多Python/compile.c,Python/symtable.c可以去哪里看看.

如果您尝试使用名为的变量,则可能会遇到一些奇怪的错误__class__:

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

Foo().f()
Run Code Online (Sandbox Code Playgroud)

输出:

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
Run Code Online (Sandbox Code Playgroud)

__class__means 的赋值__class__是局部变量而不是闭包变量,因此闭包单元格super()不需要.

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

    Foo().f()

f()
Run Code Online (Sandbox Code Playgroud)

输出:

<class '__main__.f.<locals>.Foo'>
Run Code Online (Sandbox Code Playgroud)

即使__class__封闭范围中存在实际变量,特殊外壳__class__意味着您获得类而不是封闭范围的变量值.


use*_*246 12

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

__class__是一个隐式的闭包引用,如果类主体中的任何方法引用或者是__class__super,则由编译器创建.这允许零参数形式super()正确地识别基于词法作用域定义的类,而用于进行当前调用的类或实例基于传递给该方法的第一个参数来识别.

  • 这两个答案都没有真正向我解释为什么在不使用 super 时需要创建引用。 (2认同)