gpt4 book ai didi

Python 类范围规则

转载 作者:IT老高 更新时间:2023-10-28 22:12:40 25 4
gpt4 key购买 nike

编辑: 看起来这是一个非常古老的“错误”,或者实际上是一个功能。参见,例如,this mail

我正在尝试了解 Python 范围规则。更准确地说,我以为我理解它们,但后来我发现了这段代码 here :

x = "xtop"
y = "ytop"
def func():
x = "xlocal"
y = "ylocal"
class C:
print(x)
print(y)
y = 1
func()

在 Python 3.4 中,输出为:

xlocal
ytop

如果我用函数替换内部类,那么它会合理地给出 UnboundLocalError。你能解释一下为什么它对类有这种奇怪的行为吗?选择这种范围规则的原因是什么?

最佳答案

TL;DR:此行为自 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_CLASSbuiltins.__build_class__ 加载到堆栈上;这是用参数 __build_class__(func, name) 调用的;其中 func 是类主体,name'C'。类体是函数func的常量#3:

>>> 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 用于从 locals 然后从 globals 加载值。然而,闭包单元既不显示为局部变量,也不显示为全局变量。

现在,因为名称 y 存储在类主体 (35) 中,所以它一直被用作不是闭包单元,而是本地/全局名称。闭包单元不会显示为类主体的局部变量。

这种行为是真实的ever since implementing PEP 227 - nested scopes .当时 BDFL 表示这不应该被修复 - 因此这 13 年多以来一直如此。


自 PEP 227 以来唯一的变化是在 Python 3 中添加了 nonlocal;如果在类体中使用它,类体可以设置包含范围内的单元格的值:

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设置值在那个牢房里;在这种情况下,没有为类 C 创建属性。

关于Python 类范围规则,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29333359/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com