代码之家  ›  专栏  ›  技术社区  ›  Charles Merriam

范围界定规则的简短描述?

  •  418
  • Charles Merriam  · 技术社区  · 17 年前

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

    如果我有一些代码:

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

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

    1. 在类命名空间中
    2. 在函数定义中
    3. for循环中的索引变量
    4. 在for循环内部

    在执行过程中,当函数 spam lambda functions 通过有点不同?

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

    7 回复  |  直到 7 年前
        1
  •  434
  •   martineau    6 年前

    实际上,Python范围解析的简明规则是 Learning Python, 3rd. Ed. . (这些规则特定于变量名,而不是属性。如果引用时没有句点,则适用这些规则。)

    • L 在函数中以任何方式指定的局部名称( def lambda ),并且在该函数中未声明为全局

    • E nclosing函数–在任何和所有静态封闭函数的局部范围内指定的名称( def 兰姆达

    • 在模块文件的顶层或通过执行 global 声明 def

    • B 内置名称模块中预先指定的内置(Python)名称: open , range SyntaxError

    那么,在

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

    这个 for 循环没有自己的命名空间。按照立法顺序,范围将是

    • def spam code3 , code4 code5 )
    • E:任何封闭函数(如果整个示例在另一个示例中) def
    • G:有吗 x 在模块中全局声明(在 code1
    • x 在Python中。

    x code2 (即使在您可能预期的情况下,请参见 Antti's answer here ).

        2
  •  158
  •   Brian    17 年前

    在您的示例中,只有3个作用域将在其中搜索x:

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

    • 内置名称空间。有一点特殊——它包含各种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 它将以类似于全局的方式运行,但用于嵌套的函数作用域。

        3
  •  113
  •   Antti Haapala -- Слава Україні    6 年前

    关于蟒蛇3号的时间还没有完全的答案,所以我在这里做了一个回答。此处描述的大部分内容在 4.2.2 Resolution of names

    阶级团体

    除此之外,还有块语句 def class ,创建一个变量范围。在Python2中,列表理解不创建变量作用域,但是在Python3中,列表理解中的循环变量是在新的作用域中创建的。

    展示班级成员的特点

    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

    >>> [ i for i in range(5) ]
    >>> i
    4
    

    这些理解可以作为一种巧妙的(或者糟糕的)方法,在Python 2中的lambda表达式中生成可修改的变量-lambda表达式确实创建了一个变量范围,如 def 语句,但在lambda中不允许使用任何语句。赋值是Python中的语句,这意味着lambda中不允许变量赋值,但列表理解是一个表达式。。。

    这种行为在Python3中已得到修复-没有理解表达式或生成器泄漏变量。


    全局实际上是指模块范围;python的主要模块是 __main__ sys.modules __主要__ 可以使用 sys.modules['__main__'] import __main__ ; 在那里访问和分配属性是完全可以接受的;它们将在主模块的全局范围内显示为变量。


    UnboundLocalError NameError

    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关键字:

    x = 5
    def foobar():
        global x
        print(x)
        x += 1
    
    foobar() # -> 5
    print(x) # -> 6
    

    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()      # -> 5 911
    print(x, y) # -> 6 13
    

    在Python2中,没有简单的方法修改封闭范围中的值;通常,这是通过具有可变值来模拟的,例如长度为1的列表:

    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
    

    nonlocal 前来救援:

    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.
    

    nonlocal documentation

    与全局语句中列出的名称不同,非局部语句中列出的名称必须引用封闭范围中预先存在的绑定(不能明确确定应在其中创建新绑定的范围)。

    非本地 with 子句,或作为函数参数)。


    __builtin__ 在Python3中,它现在被称为 builtins


    print 陈述在Python2.6-2.7中,您可以掌握Python3 打印 功能与:

    import __builtin__
    
    print3 = __builtin__.__dict__['print']
    

    from __future__ import print_function 实际上不导入 函数在Python2中的任意位置-相反,它只是禁用 当前模块中的语句,处理 打印 该函数可以在内置中查找。

        4
  •  24
  •   martineau    8 年前

    更完整的范围示例:

    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
    
        5
  •  23
  •   Jeremy Cantrell    17 年前

    Python2.x的范围规则已经在其他答案中进行了概述。我唯一要补充的是,在Python3.0中,还有非局部作用域的概念(由“nonlocal”关键字表示)。这允许您直接访问外部作用域,并提供了一些巧妙的技巧,包括词法闭包(没有涉及可变对象的难看的hack)。

    PEP 关于这方面的更多信息。

        6
  •  13
  •   Community Mohan Dere    9 年前

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

    在执行过程中的任何时候 名称空间可以直接访问: 首先,包含本地名称;这个 从 最近封闭范围;中间 当前模块的全局名称;和 最外层的范围(最后搜索)是

    有两个功能: globals locals

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

    x

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

    全球是——嗯——全球的。

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

    没有其他作用域。这个 for if try )不要创建新的嵌套作用域。仅定义(包、模块、函数、类和对象实例)

    code2 例如,必须由类名限定。通常地 Foo.code2 self.code2

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

    在类方法中,有局部变量和全局变量。你说 self.variable 选择实例作为命名空间。你会注意到的 self

    看见 Python Scope Rules Python Scope Variable Scope .

        7
  •  9
  •   bobince    17 年前

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

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

    lambda与def没有什么不同。如果在函数中使用lambda,则与定义嵌套函数相同。在Python2.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
    

    0 0
    
        8
  •  3
  •   MisterMiyagi    6 年前

    这个 Python name resolution

    1. Builtin Functions 例如 print , int zip ,
    2. 可以相互嵌套的三个用户定义的作用域,即
      1. 闭包范围,从任何封闭 def lambda 表达或理解。
      2. 局部范围,在 def 兰姆达 表达或理解,
      3. 范围,在 class

    值得注意的是,其他构造,例如 if , for with 语句没有自己的作用域。

    字体 查找 名称的范围从使用名称的范围开始,然后是模块全局的任何封闭范围(不包括类范围),最后是使用此搜索顺序中第一个匹配项的内置项。 这个 “到范围”默认为特殊窗体的当前范围 nonlocal global 必须习惯于 分配 从外部作用域中删除名称。

    最后,理解和生成器表达式以及 :=


    嵌套作用域和名称解析

    这些不同的作用域构建了一个层次结构,内置的、全局的始终构成基础,闭包、局部变量和类作用域嵌套为 词法上

    print("builtins are available without definition")
    
    some_global = "1"  # global variables are at module scope
    
    def outer_function():
        some_closure = "3.1"  # locals and closure are defined the same, at function scope
        some_local = "3.2"    # a variable becomes a closure if a nested scope uses it
    
        class InnerClass:
             some_classvar = "3.3"   # class variables exist *only* at class scope
    
             def nested_function(self):
                 some_local = "3.2"   # locals can replace outer names
                 print(some_closure)  # closures are always readable
        return InnerClass
    

    尽管 作用域对封闭的作用域不可见。这将创建以下层次结构:

    ┎ builtins           [print, ...]
    ┗━┱ globals            [some_global]
      ┗━┱ outer_function     [some_local, some_closure]
        ┣━╾ InnerClass         [some_classvar]
        ┗━╾ inner_function     [some_local]
    

    名称解析始终从 当前范围 some_local 在…内 outer_function inner_function 从相应的函数开始-并立即查找 一些当地人 定义于 外函数 内函数 some_closure 打印 内函数 搜索到 外函数


    范围声明和名称绑定

    一些当地人 在这两者中分别存在 内函数 对于 具有 del 也算作名称绑定。

    当名称必须引用外部变量时 如果要绑定到内部作用域,则必须将名称声明为非本地。不同类型的封闭作用域存在不同的声明: 非本地 全球的 始终引用全局名称。尤其是 非本地 从不引用全局名称和

    
    some_global = "1"
    
    def outer_function():
        some_closure = "3.2"
        some_global = "this is ignored by a nested global declaration"
        
        def inner_function():
            global some_global     # declare variable from global scope
            nonlocal some_closure  # declare variable from enclosing scope
            message = " bound by an inner scope"
            some_global = some_global + message
            some_closure = some_closure + message
        return inner_function
    

    值得注意的是,该函数是局部函数和局部函数 非本地 名称 必须 全球的


    理解和赋值表达式

    列表、集合和dict理解以及生成器表达式的范围规则如下 几乎 几乎 与常规名称绑定相同。

    理解和生成器表达式的范围与函数范围是相同的。范围中绑定的所有名称,即迭代变量,都是理解/生成器和嵌套范围的局部变量或闭包。所有名称(包括iterables)都使用函数内部适用的名称解析进行解析。

    some_global = "global"
    
    def outer_function():
        some_closure = "closure"
        return [            # new function-like scope started by comprehension
            comp_local      # names resolved using regular name resolution
            for comp_local  # iteration targets are local
            in "iterable"
            if comp_local in some_global and comp_local in some_global
        ]
    

    := 赋值表达式适用于最近的函数、类或全局范围。特别是,如果已声明赋值表达式的目标 全球的 在最近的范围内,赋值表达式与常规赋值一样尊重这一点。

    print(some_global := "global")
    
    def outer_function():
        print(some_closure := "closure")
    

    但是,理解/生成器中的赋值表达式在最近的 理解/生成器的范围,而不是理解/生成器本身的范围。嵌套多个理解/生成器时,将使用最近的函数或全局作用域。由于理解/生成器作用域可以读取闭包和全局变量,因此赋值变量在理解中也是可读的。从理解分配到类范围无效。

    print(some_global := "global")
    
    def outer_function():
        print(some_closure := "closure")
        steps = [
            # v write to variable in containing scope
            (some_closure := some_closure + comp_local)
            #                 ^ read from variable in containing scope
            for comp_local in some_global
        ]
        return some_closure, steps
    

    虽然迭代变量是其绑定的理解的局部变量,但赋值表达式的目标并不创建局部变量,而是从外部范围读取:

    ┎ builtins           [print, ...]
    ┗━┱ globals            [some_global]
      ┗━┱ outer_function     [some_closure]
        ┗━╾ <listcomp>         [comp_local]
    
        9
  •  1
  •   GraceMeng    7 年前

    在Python中,

    此时将显示作业。

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