Python类作用域规则

2024-10-03 21:33:01 发布

您现在位置:Python中文网/ 问答频道 /正文

编辑:看起来这是一个非常古老的“bug”,或者实际上是一个特性。例如,见this mail

我试图理解Python的作用域规则。更确切地说,我以为我能理解它们,但后来我发现了这个代码here

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

在python3.4中,输出是:

^{pr2}$

如果我用一个函数替换内部类,那么它合理地给出UnboundLocalError。你能解释一下它为什么对类表现出这种奇怪的方式吗?为什么选择范围规则?在


Tags: 代码编辑here规则defmail特性this
2条回答

首先关注函数内函数的闭包情况:

x = "xtop"
y = "ytop"
def func():
    x = "xlocal"
    y = "ylocal"
    def inner():
 #       global y
        print(x)
        print(y)
        y='inner y'
        print(y)
    inner()  

注意被注释掉的globalinner中,如果您运行这个,它将复制您得到的UnboundLocalError。为什么?在

跑dis.dis公司上面写着:

^{pr2}$

注意xfunc内部y的不同访问模式。在inner中使用y='inner y'创建了{}

现在取消对inner内部的global y的注释。现在,您已经明确地创建了y作为顶级全球版本,直到您辞去{}

如果global未注释,则打印:

xlocal
ytop
inner y

您可以通过以下方式获得更合理的结果:

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()    

印刷品:

xtop ytop
xlocal ylocal
xlocal inner y

闭包类的分析由于实例和类变量以及裸类(没有实例)执行的时间和时间而变得复杂。在

底线是一样的:如果你引用了本地命名空间之外的一个名称,然后在本地分配给同一个名称,你会得到一个令人惊讶的结果。在

“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) 

印刷品:

xlocal ylocal
xlocal Inner_y

您可以在PEP 3104中阅读有关Python3范围规则的更多信息

TL;DR:这种行为从python2.1PEP 227: Nested Scopes就已经存在了,并且在那时就已经知道了。如果一个名称在类主体中被赋值(比如y),那么它就被认为是一个局部/全局变量;如果它没有被赋值给(x),那么它也可能指向一个闭包单元。词法变量不会作为类主体的局部/全局名称显示。在


在Python3.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是{}。类主体是函数func的常量#3:

^{pr2}$

在类主体中,x是用LOAD_CLASSDEREF(15)访问的,而y是用LOAD_NAME(25)加载的。LOAD_CLASSDEREF是一个python3.4+操作码,用于从类主体内的闭包单元加载值(在以前的版本中,使用了泛型的^{);而{}用于从局部变量加载值,然后从全局加载值。然而,闭合单元既不显示为局部的,也不显示为全局的。在

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

这种行为是正确的ever since implementing PEP 227 - nested scopes。当时BDFL表示,这不应该是固定的,因此这13年来一直如此。在


自PEP 227以来的唯一变化是在Python3中添加了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创建属性。在

相关问题 更多 >