编辑:看起来这是一个非常古老的"错误"或实际上,功能.例如,参见这封邮件
我试图了解Python范围规则.更准确地说,我认为我理解它们,但后来我在这里找到了这个代码:
x = "xtop"
y = "ytop"
def func():
x = "xlocal"
y = "ylocal"
class C:
print(x)
print(y)
y = 1
func()
Run Code Online (Sandbox Code Playgroud)
在Python 3.4中,输出是:
xlocal
ytop
Run Code Online (Sandbox Code Playgroud)
如果我用函数替换内部类,那么它合理地给出了UnboundLocalError
.你能解释一下为什么它在课堂上表现出这种奇怪的方式吗?选择范围规则的原因是什么?
Ant*_*ala 13
TL; DR:自Python 2.1 PEP 227:嵌套作用域以来,这种行为已经存在,并且当时就已为人所知.如果在类体(如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
Run Code Online (Sandbox Code Playgroud)
该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
Run Code Online (Sandbox Code Playgroud)
在类体内,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()
Run Code Online (Sandbox Code Playgroud)
现在的输出是
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'
Run Code Online (Sandbox Code Playgroud)
也就是说,print(y)
读取y
包含范围的单元格y = 1
的值,并设置该单元格中的值; 在这种情况下,没有为该类创建任何属性C
.
首先关注闭包的情况 - 函数中的函数:
x = "xtop"
y = "ytop"
def func():
x = "xlocal"
y = "ylocal"
def inner():
# global y
print(x)
print(y)
y='inner y'
print(y)
inner()
Run Code Online (Sandbox Code Playgroud)
注意注释掉global
的inner
.如果你运行它,它复制了UnboundLocalError
你了.为什么?
在其上运行dis.dis:
>>> import dis
>>> dis.dis(func)
6 0 LOAD_CONST 1 ('xlocal')
3 STORE_DEREF 0 (x)
7 6 LOAD_CONST 2 ('ylocal')
9 STORE_FAST 0 (y)
8 12 LOAD_CLOSURE 0 (x)
15 BUILD_TUPLE 1
18 LOAD_CONST 3 (<code object inner at 0x101500270, file "Untitled 3.py", line 8>)
21 LOAD_CONST 4 ('func.<locals>.inner')
24 MAKE_CLOSURE 0
27 STORE_FAST 1 (inner)
14 30 LOAD_FAST 1 (inner)
33 CALL_FUNCTION 0 (0 positional, 0 keyword pair)
36 POP_TOP
37 LOAD_CONST 0 (None)
40 RETURN_VALUE
Run Code Online (Sandbox Code Playgroud)
注意x
vs y
内部的不同访问模式func
.y='inner y'
内部的使用inner
创造了UnboundLocalError
现在global y
在内部取消注释inner
.现在你明确地创建y
了一个顶级的全局版本,直到辞职为止y='inner y'
使用未global
注释的打印件:
xlocal
ytop
inner y
Run Code Online (Sandbox Code Playgroud)
您可以通过以下方式获得更明智的结果:
x = "xtop"
y = "ytop"
def func():
global y, x
print(x,y)
x = "xlocal"
y = "ylocal"
def inner():
global y
print(x,y)
y = 'inner y'
print(x,y)
inner()
Run Code Online (Sandbox Code Playgroud)
打印:
xtop ytop
xlocal ylocal
xlocal inner y
Run Code Online (Sandbox Code Playgroud)
闭包类的分析因实例与类变量以及正在执行裸体类(没有实例)的内容而变得复杂.
底线是相同的:如果您引用本地命名空间之外的名称,然后在本地分配相同的名称,您会得到一个令人惊讶的结果.
'fix'是相同的:使用global关键字:
x = "xtop"
y = "ytop"
def func():
global x, y
x = "xlocal"
y = "ylocal"
class Inner:
print(x, y)
y = 'Inner_y'
print(x, y)
Run Code Online (Sandbox Code Playgroud)
打印:
xlocal ylocal
xlocal Inner_y
Run Code Online (Sandbox Code Playgroud)
您可以在PEP 3104中阅读有关Python 3范围规则的更多信息
归档时间: |
|
查看次数: |
1216 次 |
最近记录: |