Python的作用域规则究竟是什么?
如果我有一些代码:
code1
class Foo:
code2
def spam.....
code3
for code4..:
code5
x()
x在哪?一些可能的选择包括以下列表:
在封闭的源文件中
在类名称空间中
在函数定义中
在for循环的索引变量中
在for循环中
还有执行期间的上下文,当函数spam被传递到其他地方时。也许lambda函数传递有点不同?
在某个地方一定有一个简单的参考或算法。对于中级Python程序员来说,这是一个令人困惑的世界。
x在哪?
X没有被找到,因为你没有定义它。:-)它可以在code1(全局)或code3(本地)中找到,如果你把它放在那里。
Code2(类成员)对于同一类的方法中的代码是不可见的——您通常会使用self访问它们。Code4 /code5(循环)与code3存在于相同的作用域中,因此如果你在那里写入x,你将改变code3中定义的x实例,而不是创建一个新的x。
Python是静态作用域的,所以如果你将' spam '传递给另一个函数,spam仍然可以访问它来自的模块中的全局变量(在code1中定义),以及任何其他包含作用域的范围(见下文)。Code2成员将再次通过self访问。
lambda与def没有区别。如果在函数中使用lambda,则与定义嵌套函数相同。在Python 2.2以后,可以使用嵌套作用域。在这种情况下,你可以在函数嵌套的任何级别绑定x, Python将会获取最内层的实例:
x= 0
def fun1():
x= 1
def fun2():
x= 2
def fun3():
return x
return fun3()
return fun2()
print fun1(), x
2 0
Fun3看到来自最近的包含作用域的实例x,该作用域是与fun2关联的函数作用域。但是在fun1和全局中定义的其他x实例不受影响。
在Python 2.1之前的nested_scopes之前,以及2.1中,除非你特别要求使用from-future-import功能,否则fun1和fun2的作用域对fun3是不可见的,所以S.Lott的答案成立,你将得到全局x:
0 0
Python通常用三个可用的名称空间来解析变量。
在执行过程中的任何时候
至少有三个嵌套作用域是谁的
命名空间可以直接访问:
最里面的作用域,用于搜索
首先,包含本地名称;的
任何封闭函数的命名空间,
从?开始搜索哪些
最近的包围范围;中间
范围,接下来搜索,包含
当前模块的全局名称;和
最外层作用域(最后搜索)是
包含内置名称的命名空间。
有两个函数:全局函数和局部函数,它们向您显示这两个名称空间的内容。
命名空间是由包、模块、类、对象结构和函数创建的。没有其他类型的名称空间。
在这种情况下,对名为x的函数的调用必须在本地名称空间或全局名称空间中解析。
在本例中,Local是方法函数Foo.spam的主体。
全球就是——嗯——全球。
规则是搜索由方法函数(以及嵌套函数定义)创建的嵌套局部空间,然后搜索全局。就是这样。
没有其他的范围了。for语句(以及其他复合语句,如if和try)不会创建新的嵌套作用域。只有定义(包、模块、函数、类和对象实例)。
在类定义中,名称是类名称空间的一部分。例如,Code2必须由类名限定。一般Foo.code2。然而,自我。code2也可以工作,因为Python对象将包含类视为一个回退。
对象(类的实例)有实例变量。这些名称位于对象的名称空间中。它们必须由对象限定。(variable.instance)。
在类方法中,有局部变量和全局变量。你说自我。变量选择实例作为命名空间。您将注意到self是每个类成员函数的参数,使其成为局部名称空间的一部分。
参见Python作用域规则、Python作用域、变量作用域。
本质上,Python中唯一引入新作用域的是函数定义。类有点特殊,因为在主体中直接定义的任何东西都放在类的名称空间中,但它们不能从它们所包含的方法(或嵌套类)中直接访问。
在你的例子中,只有3个范围x将被搜索:
垃圾邮件的作用域——包含code3和code5(以及循环变量code4)中定义的所有内容
全局作用域——包含代码1中定义的所有内容,以及Foo(以及其后的任何更改)
内置命名空间。有点特殊的情况-它包含各种Python内置函数和类型,如len()和str()。一般来说,任何用户代码都不应该修改它,所以期望它只包含标准函数。
只有在向图中引入嵌套函数(或lambda)时,才会出现更多作用域。
不过,它们的表现和你预期的差不多。嵌套函数可以访问局部作用域中的所有内容,也可以访问封闭函数作用域中的任何内容。如。
def foo():
x=4
def bar():
print x # Accesses x from foo's scope
bar() # Prints 4
x=5
bar() # Prints 5
限制:
可以访问局部函数变量以外的作用域中的变量,但不能在没有进一步语法的情况下反弹到新参数。相反,赋值将创建一个新的局部变量,而不是影响父作用域中的变量。例如:
global_var1 = []
global_var2 = 1
def func():
# This is OK: It's just accessing, not rebinding
global_var1.append(4)
# This won't affect global_var2. Instead it creates a new variable
global_var2 = 2
local1 = 4
def embedded_func():
# Again, this doen't affect func's local1 variable. It creates a
# new local variable also called local1 instead.
local1 = 5
print local1
embedded_func() # Prints 5
print local1 # Prints 4
为了从函数作用域中实际修改全局变量的绑定,需要使用global关键字指定变量为全局变量。例如:
global_var = 4
def change_global():
global global_var
global_var = global_var + 1
目前还没有办法对封闭函数作用域中的变量做同样的事情,但Python 3引入了一个新的关键字“nonlocal”,它将以类似于global的方式起作用,但用于嵌套函数作用域。
一个稍微完整一点的范围示例:
from __future__ import print_function # for python 2 support
x = 100
print("1. Global x:", x)
class Test(object):
y = x
print("2. Enclosed y:", y)
x = x + 1
print("3. Enclosed x:", x)
def method(self):
print("4. Enclosed self.x", self.x)
print("5. Global x", x)
try:
print(y)
except NameError as e:
print("6.", e)
def method_local_ref(self):
try:
print(x)
except UnboundLocalError as e:
print("7.", e)
x = 200 # causing 7 because has same name
print("8. Local x", x)
inst = Test()
inst.method()
inst.method_local_ref()
输出:
1. Global x: 100
2. Enclosed y: 100
3. Enclosed x: 101
4. Enclosed self.x 101
5. Global x 100
6. global name 'y' is not defined
7. local variable 'x' referenced before assignment
8. Local x 200