这条消息有点长,有很多例子,但我希望它能帮助我和其他人更好地掌握 Python 2.7 中变量和属性查找的完整故事。
对于代码块(例如模块、类定义、函数定义等)和变量绑定(例如作为赋值、参数声明、类和函数声明、for循环等)
我将术语变量用于可以不带点调用的名称,以及需要用对象名称限定的名称的属性(例如对象 obj 的属性 x 的 obj.x)。
Python 中的所有代码块都有三个作用域,但函数:
- 当地的
- 全球的
- 内置
Python 中有四个块仅用于函数(根据 PEP 227):
- 当地的
- 封闭函数
- 全球的
- 内置
将变量绑定到并在块中找到它的规则非常简单:
- 任何将变量绑定到块中的对象都会使该变量成为该块的局部变量,除非该变量被声明为全局变量(在这种情况下,该变量属于全局范围)
- 使用规则 LGB(本地、全局、内置)为所有块查找对变量的引用,但函数
- 仅对函数使用规则 LEGB(本地、封闭、全局、内置)来查找对变量的引用。
让我知道以验证此规则的示例,并展示许多特殊情况。对于每个例子,我都会给出我的理解。如果我错了,请纠正我。对于最后一个例子,我不明白结果。
示例 1:
x = "x in module"
class A():
print "A: " + x #x in module
x = "x in class A"
print locals()
class B():
print "B: " + x #x in module
x = "x in class B"
print locals()
def f(self):
print "f: " + x #x in module
self.x = "self.x in f"
print x, self.x
print locals()
>>>A.B().f()
A: x in module
{'x': 'x in class A', '__module__': '__main__'}
B: x in module
{'x': 'x in class B', '__module__': '__main__'}
f: x in module
x in module self.x in f
{'self': <__main__.B instance at 0x00000000026FC9C8>}
类没有嵌套范围(规则 LGB),并且类中的函数在不使用限定名称(本例中为 self.x)的情况下无法访问类的属性。这在 PEP227 中有很好的描述。
示例 2:
z = "z in module"
def f():
z = "z in f()"
class C():
z = "z in C"
def g(self):
print z
print C.z
C().g()
f()
>>>
z in f()
z in C
这里使用 LEGB 规则查找函数中的变量,但如果路径中存在类,则跳过类参数。这也是 PEP 227 所解释的。
示例 3:
var = 0
def func():
print var
var = 1
>>> func()
Traceback (most recent call last):
File "<pyshell#102>", line 1, in <module>
func()
File "C:/Users/aa/Desktop/test2.py", line 25, in func
print var
UnboundLocalError: local variable 'var' referenced before assignment
我们期望使用诸如 python 之类的动态语言来动态解决所有问题。但对于函数来说,情况并非如此。局部变量在编译时确定。PEP 227 和 http://docs.python.org/2.7/reference/executionmodel.html以这种方式描述了这种行为
“如果名称绑定操作发生在代码块内的任何位置,则块内名称的所有使用都将被视为对当前块的引用。”
示例 4:
x = "x in module"
class A():
print "A: " + x
x = "x in A"
print "A: " + x
print locals()
del x
print locals()
print "A: " + x
>>>
A: x in module
A: x in A
{'x': 'x in A', '__module__': '__main__'}
{'__module__': '__main__'}
A: x in module
但是我们在这里看到 PEP227 中的这条语句“如果名称绑定操作发生在代码块中的任何位置,则块中名称的所有使用都被视为对当前块的引用。” 当代码块是一个类时是错误的。此外,对于类,似乎本地名称绑定不是在编译时进行的,而是在执行期间使用类命名空间进行的。在这方面,PEP227 和 Python 文档中的执行模型具有误导性,并且在某些部分是错误的。
示例 5:
x = 'x in module'
def f2():
x = 'x in f2'
def myfunc():
x = 'x in myfunc'
class MyClass(object):
x = x
print x
return MyClass
myfunc()
f2()
>>>
x in module
我对这段代码的理解如下。指令 x = x 首先查找表达式右手 x 所指的对象。在这种情况下,对象在类中本地查找,然后按照规则 LGB 在全局范围内查找,即字符串“模块中的 x”。然后在类字典中创建 MyClass 的局部属性 x 并指向字符串对象。
示例 6:
现在这是一个我无法解释的例子。它非常接近示例 5,我只是将本地 MyClass 属性从 x 更改为 y。
x = 'x in module'
def f2():
x = 'x in f2'
def myfunc():
x = 'x in myfunc'
class MyClass(object):
y = x
print y
return MyClass
myfunc()
f2()
>>>
x in myfunc
为什么在这种情况下 MyClass 中的 x 引用在最里面的函数中查找?