范围规则的简短描述?

Python

十三

2020-07-27

Python范围规则到底什么

如果我有一些代码:

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

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

  1. 在随附的源文件中
  2. 在类命名空间中
  3. 在函数定义中
  4. 在for循环中索​​引变量
  5. 在for循环内

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

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

第4259篇《范围规则的简短描述?》来自Winter(https://github.com/aiyld/aiyld.github.io)的站点

3个回答
番长樱梅 2020.07.27

Python的名称解析只知道以下几种范围:

  1. 建宏范围它提供了内建函数,如printint,或zip
  2. 模块全局范围,始终是当前模块的顶层,
  3. 可以相互嵌套的三个用户定义的范围,即
    1. 函数封闭范围,来自任何封闭的def块,lambda表达式或理解。
    2. def块,lambda表达式或理解内的函数局部作用域
    3. 范围,在一个class块内。

值得注意的是,其他的结构,例如ifforwith语句不会有自己的范围。

TLDR范围界定名称查找始于使用该名称的范围,然后是所有封闭范围(不包括类范围),直至模块全局变量,最后是内置函数–使用此搜索顺序中的第一个匹配项。默认情况下,对作用域分配是当前作用域-特殊形式nonlocalglobal必须用于从外部作用域分配名称。

最后,综合起来时,理解和生成器表达式以及:=赋值表达式具有一个特殊的规则。


嵌套范围和名称解析

这些不同的作用域构建了一个层次结构,其中内置的变量然后全局变量始终构成基础,而闭包,局部变量和类作用域则按照词汇定义进行嵌套也就是说,仅源代码中的嵌套很重要,例如调用堆栈不重要。

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

即使class创建了作用域并且可能具有嵌套的类,函数和理解,class作用域的名称对于封闭的作用域也不可见。这将创建以下层次结构:

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

Name resolution always starts at the current scope in which a name is accessed, then goes up the hierarchy until a match is found. For example, looking up some_local inside outer_function and inner_function starts at the respective function - and immediately finds the some_local defined in outer_function and inner_function, respectively. When a name is not local, it is fetched from the nearest enclosing scope that defines it – looking up some_closure and print inside inner_function searches until outer_function and builtins, respectively.


Scope Declarations and Name Binding

By default, a name belongs to any scope in which it is bound to a value. Binding the same name again in an inner scope creates a new variable with the same name - for example, some_local exists separately in both outer_function and inner_function. As far as scoping is concerned, binding includes any statement that sets the value of a name – assignment statements, but also the iteration variable of a for loop, or the name of a with context manager. Notably, del also counts as name binding.

When a name must refer to an outer variable and be bound in an inner scope, the name must be declared as not local. Separate declarations exists for the different kinds of enclosing scopes: nonlocal always refers to the nearest closure, and global always refers to a global name. Notably, nonlocal never refers to a global name and global ignores all closures of the same name. There is no declaration to refer to the builtin scope.


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

Of note is that function local and nonlocal are resolved at compile time. A nonlocal name must exist in some outer scope. In contrast, a global name can be defined dynamically and may be added or removed from the global scope at any time.


Comprehensions and Assignment Expressions

The scoping rules of list, set and dict comprehensions and generator expressions are almost the same as for functions. Likewise, the scoping rules for assignment expressions are almost the same as for regular name binding.

The scope of comprehensions and generator expressions is of the same kind as function scope. All names bound in the scope, namely the iteration variables, are locals or closures to the comprehensions/generator and nested scopes. All names, including iterables, are resolved using name resolution as applicable inside functions.

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
    ]

An := assignment expression works on the nearest function, class or global scope. Notably, if the target of an assignment expression has been declared nonlocal or global in the nearest scope, the assignment expression honors this like a regular assignment.

print(some_global := "global")

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

However, an assignment expression inside a comprehension/generator works on the nearest enclosing scope of the comprehension/generator, not the scope of the comprehension/generator itself. When several comprehensions/generators are nested, the nearest function or global scope is used. Since the comprehension/generator scope can read closures and global variables, the assignment variable is readable in the comprehension as well. Assigning from a comprehension to a class scope is not valid.

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]
小卤蛋 2020.07.27

在Python中,

分配了值的任何变量对于分配在其中出现的块都是局部的。

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

斯丁前端 2020.07.27

实际上,这是学习Python的第3条关于Python范围解析的简明规则埃德 (这些规则特定于变量名,而不是属性。如果不加句点引用,则适用这些规则。)

LEGB规则

  • L ocal —在函数(deflambda)中以任何方式分配的名称,但未在该函数中声明为全局

  • E nclosing-function —在任何和所有静态封闭函数(deflambda的本地范围内从内部到外部分配的名称

  • ģ叶形(模块) -在模块文件的顶层分配名称,或通过执行global在声明def文件中

  • : - uilt式(Python)的名称在内置模块的名称预先分配的openrangeSyntaxError,等

因此,在

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

for循环中没有自己的名字空间。按照LEGB顺序,范围为

  • L:本地的def spam(在code3code4code5
  • E:任何封闭函数(如果整个示例都在另一个示例中def
  • G:x模块中的全局中是否有任何声明code1
  • B:任何内置x的Python。

x永远不会被找到code2(即使您可能会期望,也请参阅Antti的答案此处)。

问题类别

JavaScript Ckeditor Python Webpack TypeScript Vue.js React.js ExpressJS KoaJS CSS Node.js HTML Django 单元测试 PHP Asp.net jQuery Bootstrap IOS Android