Python的作用域规则究竟是什么?

如果我有一些代码:

code1
class Foo:
   code2
   def spam.....
      code3
      for code4..:
       code5
       x()

x在哪?一些可能的选择包括以下列表:

在封闭的源文件中 在类名称空间中 在函数定义中 在for循环的索引变量中 在for循环中

还有执行期间的上下文,当函数spam被传递到其他地方时。也许lambda函数传递有点不同?

在某个地方一定有一个简单的参考或算法。对于中级Python程序员来说,这是一个令人困惑的世界。


当前回答

Python 2的作用域规则。X已经在其他答案中概述了。我唯一要补充的是,在Python 3.0中,还有一个非本地作用域的概念(由'nonlocal'关键字表示)。这允许您直接访问外部作用域,并提供了执行一些巧妙技巧的能力,包括词法闭包(没有涉及可变对象的丑陋操作)。

编辑:这里是PEP的更多信息。

其他回答

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 2的作用域规则。X已经在其他答案中概述了。我唯一要补充的是,在Python 3.0中,还有一个非本地作用域的概念(由'nonlocal'关键字表示)。这允许您直接访问外部作用域,并提供了执行一些巧妙技巧的能力,包括词法闭包(没有涉及可变对象的丑陋操作)。

编辑:这里是PEP的更多信息。

本质上,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