在Python3的无参数super
实现中,这是一个奇怪的交互.在方法中访问super
会触发添加一个隐藏的__class__
闭包变量,该变量引用定义该方法的类.解析器通过向方法的符号表中添加__class__
来对方法中的名称super
进行特殊处理,然后相关代码的其余部分都会查找__class__
而不是super
.然而,如果你自己try 访问__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
和Python/symtable.c
是你想看更多的地方.
如果try 使用名为__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__
的特殊大小写意味着您得到的是类,而不是封闭范围内的变量值.