Python范围规则到底是什么?
如果我有一些代码:
code1
class Foo:
code2
def spam.....
code3
for code4..:
code5
x()
在哪里x
找到?一些可能的选择包括以下列表:
- 在随附的源文件中
- 在类命名空间中
- 在函数定义中
- 在for循环中索引变量
- 在for循环内
当函数spam
传递到其他地方时,执行期间还会有上下文。也许lambda函数传递的方式有所不同?
某个地方必须有一个简单的参考或算法。对于中级Python程序员而言,这是一个令人困惑的世界。
在Python的名称解析只知道以下几种范围:
print
,int
,或zip
,def
块,lambda
表达式或理解。def
块,lambda
表达式或理解内的函数局部作用域,class
块内。值得注意的是,其他的结构,例如
if
,for
或with
语句不会有自己的范围。TLDR范围界定:名称的查找始于使用该名称的范围,然后是所有封闭范围(不包括类范围),直至模块全局变量,最后是内置函数–使用此搜索顺序中的第一个匹配项。默认情况下,对作用域的分配是当前作用域-特殊形式
nonlocal
,global
必须用于从外部作用域分配名称。最后,综合起来时,理解和生成器表达式以及
:=
赋值表达式具有一个特殊的规则。嵌套范围和名称解析
这些不同的作用域构建了一个层次结构,其中内置的变量然后全局变量始终构成基础,而闭包,局部变量和类作用域则按照词汇定义进行嵌套。也就是说,仅源代码中的嵌套很重要,例如调用堆栈不重要。
即使
class
创建了作用域并且可能具有嵌套的类,函数和理解,class
作用域的名称对于封闭的作用域也不可见。这将创建以下层次结构: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
insideouter_function
andinner_function
starts at the respective function - and immediately finds thesome_local
defined inouter_function
andinner_function
, respectively. When a name is not local, it is fetched from the nearest enclosing scope that defines it – looking upsome_closure
andprint
insideinner_function
searches untilouter_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 bothouter_function
andinner_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 afor
loop, or the name of awith
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, andglobal
always refers to a global name. Notably,nonlocal
never refers to a global name andglobal
ignores all closures of the same name. There is no declaration to refer to the builtin scope.Of note is that function local and
nonlocal
are resolved at compile time. Anonlocal
name must exist in some outer scope. In contrast, aglobal
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.
An
:=
assignment expression works on the nearest function, class or global scope. Notably, if the target of an assignment expression has been declarednonlocal
orglobal
in the nearest scope, the assignment expression honors this like a regular assignment.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.
虽然迭代变量对于绑定它的理解是局部的,但是赋值表达式的目标不会创建局部变量,而是从外部范围读取: