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

如果我有一些代码:

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

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

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

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

在某个地方一定有一个简单的参考或算法。对于中级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的方式起作用,但用于嵌套函数作用域。

其他回答

在Python中,

任何被赋值的变量都是所在块的局部变量 作业出现了。

如果在当前范围内找不到变量,请参考LEGB顺序。

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

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

一个稍微完整一点的范围示例:

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

本质上,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的方式起作用,但用于嵌套函数作用域。

Python通常用三个可用的名称空间来解析变量。

在执行过程中的任何时候 至少有三个嵌套作用域是谁的 命名空间可以直接访问: 最里面的作用域,用于搜索 首先,包含本地名称;的 任何封闭函数的命名空间, 从?开始搜索哪些 最近的包围范围;中间 范围,接下来搜索,包含 当前模块的全局名称;和 最外层作用域(最后搜索)是 包含内置名称的命名空间。

有两个函数:全局函数和局部函数,它们向您显示这两个名称空间的内容。

命名空间是由包、模块、类、对象结构和函数创建的。没有其他类型的名称空间。

在这种情况下,对名为x的函数的调用必须在本地名称空间或全局名称空间中解析。

在本例中,Local是方法函数Foo.spam的主体。

全球就是——嗯——全球。

规则是搜索由方法函数(以及嵌套函数定义)创建的嵌套局部空间,然后搜索全局。就是这样。

没有其他的范围了。for语句(以及其他复合语句,如if和try)不会创建新的嵌套作用域。只有定义(包、模块、函数、类和对象实例)。

在类定义中,名称是类名称空间的一部分。例如,Code2必须由类名限定。一般Foo.code2。然而,自我。code2也可以工作,因为Python对象将包含类视为一个回退。

对象(类的实例)有实例变量。这些名称位于对象的名称空间中。它们必须由对象限定。(variable.instance)。

在类方法中,有局部变量和全局变量。你说自我。变量选择实例作为命名空间。您将注意到self是每个类成员函数的参数,使其成为局部名称空间的一部分。

参见Python作用域规则、Python作用域、变量作用域。