范围规则的简短描述?
Python范围规则究竟是什么?
如果我有一些代码:
code1
class Foo:
code2
def spam.....
code3
for code4..:
code5
x()
x在哪里找到? 一些可能的选择包括上面的列表:
在执行过程中还有上下文,当功能垃圾邮件被传递到其他地方时。 也许lambda函数有点不同?
某处必须有一个简单的参考或算法。 对于中级Python程序员来说,这是一个混乱的世界。
实际上,Python Python的第3部分提供了一个Python范围解析的简明规则。 Ed ..(这些规则是特定于变量名称的,而不是属性的,如果你引用它没有句点,这些规则适用)
LEGB规则。
L ,Local - 在函数中以任何方式分配的名称( def
或lambda
)),而不是在该函数中声明为全局的。
E ,Enclosing-function locals - 任意和所有静态封闭函数( def
或lambda
)的局部范围内的名称,从内部到外部。
G ,Global(模块) - 在模块文件的顶层分配的名称,或者通过在文件中的def
执行global
语句。
B ,内置(Python) - 内置名称模块中预先指定的名称: open
, range
, SyntaxError
,...
所以,在这种情况下
code1
class Foo:
code2
def spam.....
code3
for code4..:
code5
x()
for循环没有自己的名字空间。 在LEGB订单中,范围将是
L:本地,在def spam
(在code3
, code 4
, code5
)。
E:封闭函数,任何封闭函数(如果整个例子在另一个def
)
G:全球。 是否有任何x
的模块(在全球范围内声明的code1
)?
B:Python中的任何内建x
。
在code2
永远不会找到x
(即使在你可能期望的情况下,请参阅Antti的答案或在这里)。
从本质上讲,引入新范围的Python中唯一的东西就是函数定义。 类是一种特殊情况,因为直接在主体中定义的任何东西都放在类的名称空间中,但是它们不能从它们包含的方法(或嵌套类)中直接访问。
在你的例子中,只有3个范围将在其中搜索x:
垃圾邮件的范围 - 包含在code3和code5中定义的所有内容(以及代码4,循环变量)
全局范围 - 包含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
”,它将以类似于全局的方式工作,但对于嵌套函数作用域。
关于Python3的时间并没有彻底的回答,所以我在这里做了一个答案。
正如其他答案中所提供的,LEGB有4个基本范围,适用于本地,封闭,全局和内置。 除此之外,还有一个特殊的范围,即班级主体 ,它不包含在班级内定义的方法的封闭范围; 类体内的任何赋值都会使变量从此处被绑定到类体中。
特别是,除def
和class
之外, 没有任何块语句会创建变量作用域。 在Python 2中,列表理解不会创建变量范围,但是在Python 3中,循环变量是在新范围内创建的。
展示班级身体的特点
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
循环不会创建变量作用域。 在Python 2中,列表解析不会创建一个范围(虽然生成器和词典解释可以做!)相反,它们会泄漏函数或全局范围中的值:
>>> [ 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) # UnboundLocalError!
x += 1 # assignment here makes x a local variable!
范围可以声明它明确地想要用global关键字修改全局(模块范围)变量:
x = 5
def foobar():
global x
print(x) # -> 5
x += 1
foobar()
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() # -> print 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.
任何不被视为局部于当前范围的变量或任何封闭范围都是全局变量。 全局名称在模块全局字典中查找; 如果没有找到,那么从内置模块查找全局; 该模块的名称已从python 2更改为python 3; 在python 2中是__builtin__
而在python 3中它现在被称为builtins
。 如果赋值给builtins模块的一个属性,那么之后它将作为一个可读的全局变量显示给任何模块,除非该模块使用它自己的具有相同名称的全局变量来隐藏它们。
读取内置模块也是有用的; 假设你想在文件的某些部分使用python 3风格的打印函数,但是如果你的python版本> = 2.6,那么文件的其他部分仍然使用print
语句,你可以得到新的风格函数:
import __builtin__
print3 = __builtin__.__dict__['print']
from __future__ import print_function
实际上并没有在Python 2中的任何地方导入print
函数 - 相反它只是禁用了当前模块中的print
语句的解析规则,像任何其他变量标识符一样处理print
,从而允许print
函数被查找在内部。