这是 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.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__
对手段的赋值__class__
是一个局部变量而不是一个闭包变量,所以闭包单元super()
需要不存在。
def f():
__class__ = 2
class Foo:
def f(self):
print(__class__)
Foo().f()
f()
输出:
<class '__main__.f.<locals>.Foo'>
即使封闭作用域中有一个实际__class__
变量,特殊情况下的__class__
意思是你得到类而不是封闭作用域的变量值。