为什么静态绑定在类和函数上表现不同?

7
在Python中(测试版本为2.7.6),所有变量都在编译时静态绑定到作用域。这个过程在http://www.python.org/dev/peps/pep-0227/http://docs.python.org/2.7/reference/executionmodel.html中有详细说明。
其中明确指出:“如果名称绑定操作在代码块内的任何地方发生,则块内所有对该名称的使用都被视为对当前块的引用。”
函数是一个代码块,因此下面的代码将失败,因为x在使用后进行了赋值(因此在编译时它被定义为局部变量,但在执行时,在绑定之前使用) 。
x = 1
def f():
    print x 
    x = 2
    print x

>>> f()

Traceback (most recent call last):
  File "<pyshell#46>", line 1, in <module>
    f()
  File "<pyshell#45>", line 2, in f
    print x
UnboundLocalError: local variable 'x' referenced before assignment

一个类也是一个代码块,因此我们应该观察完全相同的行为。但这不是我所观察到的。 看这个例子:

x = 1
class C():
    y = x + 10
    x = 2
    def __init__(self):
        print C.y

>>> C.x
2
>>> C.y
11      
>>> C()
11
<__main__.C instance at 0x00000000027CC9C8>

作为类定义是一个代码块,此块内的任何赋值操作都应该使得变量成为局部变量。因此,x 应该是类 C 的局部变量,那么 y = x + 10 应该会导致一个 UnboundLocalError 错误。为什么没有出现这样的错误呢?

这个线程https://dev59.com/CGnWa4cB1Zd3GeqP0Gfn提到了同样的问题,但没有给出强有力的解释。PEP-0227和Python 2.7执行模型在函数块或类块之间没有任何区别。 - user3022222
这是一个常见的作用域问题。请注意以下两者之间名称空间的差异:<function f at 0x1092a80c8><class __main__.C at 0x1092946d0>。区别在于 __main__ 名称空间。 - VooDooNOFX
此外,这与https://dev59.com/CGnWa4cB1Zd3GeqP0Gfn非常密切相关。 - VooDooNOFX
@VooDooNOFX,我还是不理解你的解释。一个类是一个代码块,而在代码块中任何地方分配的变量都是局部变量。但这并不是我观察到的情况。似乎只有函数才有静态绑定,但我在 Python 文档中没有找到任何对该行为的参考说明。 - user3022222
1个回答

3

是的——似乎文档有点误导。类定义实际上与其他普通块不完全相同:

global_one = 0

class A(object):
    x = global_one + 10
    global_one = 100
    y = global_one + 20
    del global_one
    z = global_one + 30

a = A()
print a.x, a.y, a.z, global_one

结果为:10、120、30、0

如果您尝试使用函数做同样的事情,您将在第一次访问global_one时收到一个UnboundLocalError

原因是正常情况下类定义可以访问父级作用域,但是所有名称赋值都不会修改本地作用域,而是被捕获到类的数据属性字典中。文档中有关于这个问题的提示,但这显然不是很明显。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接