关于python:范围规则的简短描述?

Short description of the scoping rules?

python的作用域规则到底是什么?

如果我有一些代码:

1
2
3
4
5
6
7
8
code1
class Foo:
   code2
   def spam.....
      code3
      for code4..:
       code5
       x()

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

  • 在封闭源文件中
  • 在类命名空间中
  • 在函数定义中
  • 在for循环索引变量中
  • 在for循环中
  • 在执行过程中,当函数spam被传递到其他地方时,也存在上下文。也许lambda函数传递的有点不同?

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


    实际上,关于Python范围解析的一个简明规则,摘自《学习Python》,第3版。(这些规则特定于变量名,而不是属性。如果没有句号,则适用本规则)

    莱布规则

    l,局部名称—在函数(deflambda中以任何方式分配的名称,但在该函数中未声明为全局名称。

    e,封闭函数locals—任何和所有静态封闭函数(deflambda的局部范围内的名称,从内到外。

    g,global(module)-在模块文件的顶层分配的名称,或通过在文件中执行def中的global语句来分配的名称。

    B,内置(python)-内置名称模块中预先指定的名称:openrangeSyntaxError…。

    所以,在

    1
    2
    3
    4
    5
    6
    7
    8
    code1
    class Foo:
       code2
       def spam.....
          code3
          for code4..:
           code5
           x()

    for循环没有自己的命名空间。按legb顺序,作用域是

    L:本地,在def spam(在code3code 4code5

    e:封闭函数,任何封闭函数(如果整个示例在另一个def中)

    G:全球。模块(code1中是否有全球宣布的x

    B:python中任何内置的x

    x永远不会出现在code2中(即使在你可能预料到的情况下,也可以在这里看到安蒂的答案)。


    从本质上讲,Python中引入新范围的唯一东西就是函数定义。类有点特殊,因为直接在主体中定义的任何内容都放在类的命名空间中,但是它们不能从包含的方法(或嵌套类)中直接访问。

    在您的示例中,只有3个范围将在其中搜索x:

    • 垃圾邮件的范围-包含在code3和code5中定义的所有内容(以及code4,循环变量)

    • 全局范围-包含代码1中定义的所有内容以及foo(以及其后的任何更改)

    • 内置命名空间。有点特殊——它包含各种Python内置函数和类型,如len()和str()。一般来说,这不应该被任何用户代码修改,所以希望它包含标准函数,而不包含其他任何函数。

    只有在图片中引入嵌套函数(或lambda)时,才会出现更多范围。不过,它们的表现将与您预期的差不多。嵌套函数可以访问本地范围内的所有内容,也可以访问封闭函数范围内的所有内容。如。

    1
    2
    3
    4
    5
    6
    7
    def foo():
        x=4
        def bar():
            print x  # Accesses x from foo's scope
        bar()  # Prints 4
        x=5
        bar()  # Prints 5

    限制:

    可以访问除局部函数变量以外的作用域中的变量,但如果没有进一步的语法,则无法恢复到新参数。相反,赋值将创建一个新的局部变量,而不是影响父范围中的变量。例如:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    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关键字指定该变量是全局变量。如:

    1
    2
    3
    4
    global_var = 4
    def change_global():
        global global_var
        global_var = global_var + 1

    目前,对于封闭函数作用域中的变量没有办法做到这一点,但是python 3引入了一个新的关键字"nonlocal",它的作用方式与全局类似,但用于嵌套函数作用域。


    关于Python3的时间,没有一个彻底的答案,所以我在这里做了一个答案。好的。

    正如其他答案中提供的,有4个基本范围,即legb,用于本地、封闭、全局和内置。除此之外,还有一个特殊的范围,即类体,它不包含在类内定义的方法的封闭范围;类体内的任何赋值都会使从此处开始的变量被绑定到类体中。好的。

    尤其是,除了defclass之外,没有块语句创建变量范围。在python2中,列表理解不会创建变量范围,但是在python3中,列表理解中的循环变量是在新范围中创建的。好的。

    展示班级的特点好的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    x = 0
    class X(object):
        y = x
        x = x + 1 # x is now a variable
        z = x

        def method(self):
            print(self.x) # -> 1
            print(x)      # -> 0, the global x
            print(y)      # -> NameError: global name 'y' is not defined

    inst = X()
    print(inst.x, inst.y, inst.z, x) # -> (1, 0, 1, 0)

    因此,与在函数体中不同,您可以将变量重新分配到类体中的相同名称,以获得具有相同名称的类变量;进一步查找此名称可以解决改为类变量。好的。

    对于许多新加入Python的人来说,最大的惊喜之一是for循环不会创建变量范围。在Python2中,列表理解也不创建范围(而生成器和dict理解也创建范围!)相反,它们会泄漏函数或全局范围中的值:好的。

    1
    2
    3
    >>> [ i for i in range(5) ]
    >>> i
    4

    这些理解可以作为一种巧妙的方法(如果你愿意的话,也可以是糟糕的)在python2中的lambda表达式中创建可修改的变量——lambda表达式确实创建了一个变量范围,就像def语句那样,但是在lambda中不允许使用任何语句。赋值是Python中的语句,意味着不允许在lambda中进行变量赋值,但列表理解是一个表达式…好的。

    这种行为已经在python3中得到了修复——没有理解表达式或生成器泄漏变量。好的。

    全局实际上是指模块的作用域;python主模块是__main__;所有导入的模块都可以通过sys.modules变量访问;要访问__main__可以使用sys.modules['__main__']import __main__;在那里访问和分配属性是完全可以接受的;它们将在全局sco中显示为变量。主模块的PE。好的。

    如果在当前作用域(类作用域除外)中曾经分配名称,则该名称将被视为属于该作用域,否则将被视为属于分配给变量的任何封闭作用域(可能尚未分配该名称,或根本没有分配该名称),或最终属于全局作用域。如果变量被视为局部变量,但尚未设置或已被删除,则读取变量值将导致UnboundLocalError,这是NameError的一个子类。好的。

    1
    2
    3
    4
    5
    6
    7
    x = 5
    def foobar():
        print(x)  # causes UnboundLocalError!
        x += 1    # because assignment here makes x a local variable within the function

    # call the function
    foobar()

    作用域可以声明它显式地想要用global关键字修改global(module scope)变量:好的。

    1
    2
    3
    4
    5
    6
    7
    8
    x = 5
    def foobar():
        global x
        print(x) # -> 5
        x += 1

    foobar()
    print(x) # -> 6

    这也是可能的,即使它被隐藏在封闭范围内:好的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    x = 5
    y = 13
    def make_closure():
        x = 42
        y = 911
        def func():
            global x # sees the global value
            print(x, y)
            x += 1

        return func

    func = make_closure()
    func()      # -> print 5 911
    print(x, y) # -> 6 13

    在python2中,修改封闭范围中的值不是一个简单的方法;通常通过一个可变的值来模拟,例如长度为1的列表:好的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    def make_closure():
        value = [0]
        def get_next_value():
            value[0] += 1
            return value[0]

        return get_next_value

    get_next = make_closure()
    print(get_next()) # -> 1
    print(get_next()) # -> 2

    但是在python3中,nonlocal来拯救:好的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    def make_closure():
        value = 0
        def get_next_value():
            nonlocal value
            value += 1
            return value
        return get_next_value

    get_next = make_closure() # identical behavior to the previous example.

    任何不被视为当前作用域或任何封闭作用域局部的变量都是全局变量。在模块全局字典中查找全局名;如果找不到全局名,则从内置模块中查找全局名;模块名从python 2更改为python 3;在python 2中,它是__builtin__,在python 3中,它现在称为builtins。如果您分配给内置模块的属性,那么它将在此后作为可读全局变量对任何模块可见,除非该模块使用自己的同名全局变量对其进行阴影处理。好的。

    读取内置模块也很有用;假设您希望在文件的某些部分使用python3样式的打印函数,但文件的其他部分仍然使用print语句。在python 2.6-2.7中,可以通过以下方式获得python 3 print函数:好的。

    1
    2
    3
    import __builtin__

    print3 = __builtin__.__dict__['print']

    from __future__ import print_function实际上并不在python 2中的任何地方导入print函数,而是在当前模块中禁用print语句的解析规则,像处理其他变量标识符一样处理print,从而允许print函数在内部查找。好的。好啊。


    python 2.x的作用域规则已经在其他答案中进行了概述。我唯一要补充的是,在python 3.0中,还有一个非本地范围的概念(由"non local"关键字表示)。这允许您直接访问外部作用域,并打开了执行一些巧妙技巧的能力,包括词汇闭包(不需要涉及可变对象的丑陋黑客)。

    编辑:这是关于这方面的更多信息的政治公众人物。


    更完整的范围示例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    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
    2
    3
    4
    5
    6
    7
    8
    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使用三个可用的名称空间来解析变量。

    At any time during execution, there
    are at least three nested scopes whose
    namespaces are directly accessible:
    the innermost scope, which is searched
    first, contains the local names; the
    namespaces of any enclosing functions,
    which are searched starting with the
    nearest enclosing scope; the middle
    scope, searched next, contains the
    current module's global names; and the
    outermost scope (searched last) is the
    namespace containing built-in names.

    有两个函数:globalslocals向您显示这些名称空间中的两个内容。

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

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

    在这种情况下,局部是方法函数Foo.spam的主体。

    全球就是——好吧——全球。

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

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

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

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

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

    参见python scope rules、python scope、variable scope。


    Where is x found?

    未找到X,因为您尚未定义它。:-)如果放在代码1(全局)或代码3(本地)中,可以找到它。

    代码2(类成员)对同一类的方法内的代码不可见?-?您通常会使用self访问它们。code4/code5(循环)与code3的作用域相同,因此如果在其中写入x,您将更改code3中定义的x实例,而不是生成新的x。

    python的作用域是静态的,所以如果您将"spam"传递给另一个函数,spam仍然可以访问它来自的模块(在code1中定义)中的全局,以及任何其他包含作用域的函数(见下文)。代码2成员将再次通过self访问。

    lambda与def没有区别。如果在函数内部使用lambda,则与定义嵌套函数相同。在python 2.2之后的版本中,可以使用嵌套范围。在这种情况下,可以在任何级别的函数嵌套中绑定x,而python将获取最内部的实例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    x= 0
    def fun1():
        x= 1
        def fun2():
            x= 2
            def fun3():
                return x
            return fun3()
        return fun2()
    print fun1(), x

    2 0

    fun3从最近的包含范围(即与fun2关联的函数范围)中查看实例x。但在fun1和global中定义的其他x实例不受影响。

    在嵌套的作用域之前?-?在python pre-2.1和2.1中,除非您特别要求使用将来导入的特性?-?fun1和fun2的作用域对fun3不可见,所以s.lott的答案是成立的,您将得到全局x:

    1
    0 0