:此行为自Python 2.1 PEP 227:Nested Scopes以来就存在,并且在那时是已知的。如果名称是在类主体中分配的(例如y
),则假定该名称是局部/全局变量;如果未将其分配给(x
),则它也可能指向闭包单元。词汇变量不会显示为类主体的本地/全局名称。
在Python 3.4上,dis.dis(func)
显示以下内容:
>>> dis.dis(func)
4 0 LOAD_CONST 1 ('xlocal')
3 STORE_DEREF 0 (x)
5 6 LOAD_CONST 2 ('ylocal')
9 STORE_FAST 0 (y)
6 12 LOAD_BUILD_CLASS
13 LOAD_CLOSURE 0 (x)
16 BUILD_TUPLE 1
19 LOAD_CONST 3 (<code object C at 0x7f083c9bbf60, file "test.py", line 6>)
22 LOAD_CONST 4 ('C')
25 MAKE_CLOSURE 0
28 LOAD_CONST 4 ('C')
31 CALL_FUNCTION 2 (2 positional, 0 keyword pair)
34 STORE_FAST 1 (C)
37 LOAD_CONST 0 (None)
40 RETURN_VALUE
将LOAD_BUILD_CLASS
负载加载builtins.__build_class__
到堆栈上;这被称为参数__build_class__(func, name)
; 这里func
是类的身体,name
是'C'
。类主体是该函数的常量#3 func
:
>>> dis.dis(func.__code__.co_consts[3])
6 0 LOAD_NAME 0 (__name__)
3 STORE_NAME 1 (__module__)
6 LOAD_CONST 0 ('func.<locals>.C')
9 STORE_NAME 2 (__qualname__)
7 12 LOAD_NAME 3 (print)
15 LOAD_CLASSDEREF 0 (x)
18 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
21 POP_TOP
8 22 LOAD_NAME 3 (print)
25 LOAD_NAME 4 (y)
28 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
31 POP_TOP
9 32 LOAD_CONST 1 (1)
35 STORE_NAME 4 (y)
38 LOAD_CONST 2 (None)
41 RETURN_VALUE
在类体内,x
通过LOAD_CLASSDEREF
(15)访问,而y
通过LOAD_NAME
(25)加载。的LOAD_CLASSDEREF
是一个Python 3.4+操作码从具体内的类主体闭合细胞(在以前的版本,通用载荷值LOAD_DEREF
被使用); 该LOAD_NAME
是从装载值 当地人 ,然后全局 。但是,闭包单元既不显示为局部变量,也不显示为全局变量。
现在,由于该名称y
存储在类主体中(35),因此该名称始终用作闭包单元而不是本地/全局名称。闭包单元格不会显示为类主体的局部变量。
自从实现PEP 227嵌套范围以来,这种行为就一直存在。当时BDFL声明不应解决此问题-因此,这已经超过13年了。
自PEP 227以来唯一的变化是nonlocal
在Python 3中的添加;如果在类主体中使用它,则该类主体可以在包含范围内设置单元格的值:
x = "xtop"
y = "ytop"
def func():
x = "xlocal"
y = "ylocal"
class C:
nonlocal y # y here Now refers to the outer variable
print(x)
print(y)
y = 1
print(y)
print(C.y)
func()
现在的输出是
xlocal
ylocal
1
Traceback (most recent call last):
File "test.py", line 15, in <module>
func()
File "test.py", line 13, in func
print(C.y)
AttributeError: type object 'C' has no attribute 'y'
也就是说,print(y)
读取y
包含作用域的单元格y = 1
的值,然后在该单元格中设置该值;在这种情况下,不会为class创建任何属性C
。