30

There's a surprise here:

>>> 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

It seems like the mere mention of __class__ is explicitly checked by the parser? Otherwise we should get something like

NameError: name '__class__' is not defined

Indeed, if you modify to only check the key instead, i.e. check for '__class__' in locals(), then we only have self in scope as expected.

How does it happen that this variable gets magically injected into scope? My guess is this is something to do with super - but I didn't use super, so why does the compiler create an implicit closure reference here if it isn't needed?

4

2 回答 2

30

这是 Python 3 的 no-argument 实现中的一种奇怪的交互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.cPython/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__意思是你得到类而不是封闭作用域的变量值。

于 2016-05-03T01:06:45.067 回答
18

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

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

于 2016-05-03T01:13:06.803 回答