问题:范围规则的简短描述?
Python范围规则到底是什么?
如果我有一些代码:
code1
class Foo:
code2
def spam.....
code3
for code4..:
code5
x()
在哪里x
找到?一些可能的选择包括以下列表:
- 在随附的源文件中
- 在类命名空间中
- 在函数定义中
- 在for循环中,索引变量
- 在for循环内
当函数spam
传递到其他地方时,执行期间还会有上下文。也许lambda函数传递的方式有所不同?
某个地方必须有一个简单的参考或算法。对于中级Python程序员而言,这是一个令人困惑的世界。
回答 0
实际上,这是学习Python的第3条关于Python范围解析的简明规则。埃德 。(这些规则特定于变量名,而不是属性。如果不加句点引用,则适用这些规则。)
LEGB规则
L ocal —在函数(
def
或lambda
)中以任何方式分配的名称,但未在该函数中声明为全局E nclosing-function —在任何和所有静态封装函数(
def
或lambda
)的本地范围内从内部到外部分配的名称ģ叶形(模块) -在模块文件的顶层分配名称,或通过执行
global
在声明def
文件中乙: – uilt式(Python)的名称在内置模块的名称预先分配的
open
,range
,SyntaxError
,等
因此,在
code1
class Foo:
code2
def spam():
code3
for code4:
code5
x()
该for
循环中没有自己的名字空间。按照LEGB顺序,范围为
- L:本地的
def spam
(在code3
,code4
和code5
) - E:任何封闭函数(如果整个示例都在另一个示例中
def
) - G:
x
模块中的全局中是否有任何声明code1
? - B:任何内置
x
的Python。
回答 1
本质上,Python中唯一引入新作用域的就是函数定义。类是一种特殊情况,因为直接在主体中定义的所有内容都放置在类的命名空间中,但是不能从它们包含的方法(或嵌套类)中直接访问它们。
在您的示例中,只有3个范围可以在其中搜索x:
垃圾邮件的范围-包含在code3和code5(以及code4,循环变量)中定义的所有内容
全局范围-包含code1中定义的所有内容以及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
”,它的作用与全局变量类似,但对于嵌套函数范围而言。
回答 2
关于Python3时间,还没有详尽的答案,所以我在这里做了一个答案。4.2.2 Python 3文档名称的解析详细介绍了此处描述的大部分内容。
如其他答案中所提供的,本地,封闭,全局和内置有4个基本范围,即LEGB。除此以外,还有一个特殊的范围,即类主体,它不包含该类中定义的方法的封闭范围;类主体内的任何赋值都会使变量从此绑定到类主体中。
特别是,除了和之外,没有块语句创建变量作用域。在Python 2中,列表推导不会创建变量作用域,但是在Python 3中,列表推导内的循环变量是在新作用域中创建的。def
class
证明Class机构的特殊性
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)
因此,与函数主体不同,您可以在类主体中将变量重新分配给相同的名称,以获得具有相同名称的类变量。对该名称的进一步查找将解析为class变量。
对于Python的许多新手来说,最大的惊喜之一就是for
循环不会创建变量作用域。在Python 2中,列表推导也不创建作用域(而generator和dict则创建!)而是泄漏函数或全局范围中的值:
>>> [ i for i in range(5) ]
>>> i
4
理解可以用作在Python 2中的lambda表达式内创建可修改变量的一种狡猾(或者如果您愿意的话)的方法-lambda表达式确实会创建变量作用域,就像该def
语句那样,但是在lambda中不允许使用任何语句。赋值是Python中的语句,表示不允许在lambda中进行变量赋值,但列表推导是一个表达式…
此行为已在Python 3中修复-没有理解表达式或生成器会泄漏变量。
全局实际上意味着模块范围;主要的python模块是__main__
; 所有导入的模块都可以通过该sys.modules
变量访问;获得__main__
可以使用的权限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
在python 2中,没有简单的方法可以在封闭范围内修改值;通常,这是通过具有可变值(例如长度为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
但是,在python 3中,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
始终是指已绑定名称的最内层外部非全局范围(即,分配给该全局for
变量,包括在with
子句中或用作函数参数,包括用作目标变量)。
任何不被认为是当前作用域或任何封闭作用域局部变量的变量都是全局变量。在模块全局词典中查找全局名称;如果找不到,则从内建模块中查找全局变量;模块的名称从python 2更改为python 3; 在python 2中曾经是__builtin__
,在python 3中现在被称为builtins
。如果分配给buildins模块的属性,此后它将以可读的全局变量的形式对任何模块可见,除非该模块用自己的同名全局变量来遮盖它们。
读取内置模块也很有用;假设您希望在文件的某些部分使用python 3样式的打印功能,但文件的其他部分仍使用该print
语句。在Python 2.6-2.7中,您可以使用以下命令来掌握Python 3 print
函数:
import __builtin__
print3 = __builtin__.__dict__['print']
该from __future__ import print_function
实际上不会导入print
功能,随时随地在Python 2 -而不是它只是禁止解析规则,print
在当前模块中的语句,处理print
像任何其他变量标识符,从而使print
功能的内建进行查找。
回答 3
其他答案中已经概述了Python 2.x的范围规则。我唯一要补充的是,在Python 3.0中,还有一个非本地范围的概念(由’nonlocal’关键字指示)。这使您可以直接访问外部作用域,并可以进行一些巧妙的技巧,包括词法关闭(没有涉及可变对象的难看的技巧)。
编辑:这是PEP,对此有更多信息。
回答 4
范围的更完整示例:
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
Python通常使用三个可用的命名空间来解析变量。
在执行过程中的任何时候,至少有三个嵌套作用域可以直接访问其命名空间:最先搜索的最内部作用域包含本地名称;任何封闭函数的命名空间,它们从最近的封闭范围开始搜索;接下来搜索的中间范围包含当前模块的全局名称;最外面的作用域(最后搜索)是包含内置名称的命名空间。
有两个函数:globals
和locals
,它们向您显示这些命名空间中的两个。
命名空间是由包,模块,类,对象构造和函数创建的。没有其他类型的命名空间。
在这种情况下,x
必须在本地命名空间或全局命名空间中解析对名为的函数的调用。
在这种情况下,局部变量是方法函数的主体Foo.spam
。
全球是-很好-全球。
规则是搜索由方法函数(和嵌套函数定义)创建的嵌套局部空间,然后全局搜索。而已。
没有其他范围。该for
语句(以及其他复合语句,如if
和try
)不会创建新的嵌套作用域。仅定义(包,模块,函数,类和对象实例。)
在类定义中,名称是类命名空间的一部分。 code2
,例如,必须由类名限定。一般而言Foo.code2
。但是,self.code2
由于Python对象会将包含的类视为备用类,因此也可以使用。
对象(类的实例)具有实例变量。这些名称位于对象的命名空间中。它们必须由对象限定。(variable.instance
。)
在类方法中,您具有局部变量和全局变量。您说self.variable
选择实例作为命名空间。您会注意到,这self
是每个类成员函数的参数,使其成为本地命名空间的一部分。
回答 6
在哪里找到x?
找不到x,因为您尚未定义x。:-)如果将其放在代码1(全局)或代码3(本地)中,则可以找到它。
code2(类成员)对于相同类的方法内部的代码不可见-您通常可以使用self来访问它们。code4 / code5(循环)的作用域与code3相同,因此,如果您在其中写入x,则将更改code3中定义的x实例,而不创建新的x。
Python是静态作用域的,因此,如果您将“垃圾邮件”传递给另一个函数,则垃圾邮件仍可访问其来源模块(在code1中定义)以及其他任何包含作用域的模块中的全局变量(请参见下文)。code2成员将再次通过self访问。
lambda和def一样。如果在函数内部使用了lambda,则与定义嵌套函数相同。从Python 2.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
fun3从最近的包含范围(与fun2关联的函数范围)中看到实例x。但是在fun1和全局中定义的其他x实例不受影响。
在nested_scopes之前(在Python 2.1之前的版本中以及在2.1中,除非您专门使用from-future-import要求功能),fun3不可见fun1和fun2的作用域,因此S.Lott的答案成立,您将获得全局x :
0 0
回答 7
在Python中,
分配了值的任何变量对于分配在其中出现的块都是局部的。
如果在当前范围内找不到变量,请参考LEGB顺序。