关于Python3时间,没有一个全面的答案,所以我在这里做了个回答。这里描述的大多数内容都在Python 3文档的4.2.2名称解析中详细说明。
如其他答案所提供的那样,LEGB是四个基本作用域,即本地作用域、嵌套作用域、全局作用域和内置作用域。除此之外,还有一个特殊的作用域,即类体,它不包括定义在类内部的方法的封闭作用域; 类体内的任何赋值都会将变量绑定到类体内。
特别地,除了def和class之外,没有任何块语句创建变量作用域。在Python 2中,列表推导式不会创建变量作用域,但是在Python 3中,列表推导式中的循环变量会创建一个新作用域。
为了展示类体的特殊性
x = 0
class X(object):
y = x
x = x + 1
z = x
def method(self):
print(self.x)
print(x)
print(y)
inst = X()
print(inst.x, inst.y, inst.z, x)
因此,在类的主体中,与函数主体不同,您可以将变量重新分配给相同的名称,以获得具有相同名称的类变量; 对此名称的进一步查找将解析为类变量。
对于许多新手来说,Python的一个更大的惊喜是,for
循环不会创建变量作用域。在Python 2中,列表推导式也不会创建作用域(而生成器和字典推导式会!)而是泄漏到函数或全局作用域中的值:
>>> [ i for i in range(5) ]
>>> i
4
Python 2中,列表推导可以用作一种巧妙(或者说糟糕)的方式,在lambda表达式中创建可修改的变量 - lambda表达式确实会创建一个变量范围,就像def
语句一样,但是在lambda中不允许任何语句。因为在Python中分配是一个语句,这意味着在lambda中不允许变量分配,但是列表推导是一个表达式...
这个行为已经在Python 3中得到解决 - 没有推导表达式或生成器泄漏变量。
全局(Global)实际上指的是模块作用域(module scope); 主要的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()
print(x)
即使在封闭作用域中被屏蔽,也是可行的:
x = 5
y = 13
def make_closure():
x = 42
y = 911
def func():
global x
print(x, y)
x += 1
return func
func = make_closure()
func()
print(x, y)
在Python 2中,没有简单的方法来修改封闭作用域中的值;通常这是通过使用可变值来模拟,例如长度为1的列表:
在 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())
print(get_next())
然而在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()
nonlocal
语句文档 表明:
在 nonlocal
语句中列出的名称,与在全局语句中列出的不同,必须引用封闭作用域中的预先存在的绑定(无法明确确定应在哪个作用域中创建新绑定)。
也就是说,nonlocal
总是指向最内层外部的非全局作用域,其中名称已被绑定(即分配给、包括用作 for
目标变量、在 with
子句中或作为函数参数使用)。
任何未被视为当前作用域或任何封闭作用域本地的变量均为全局变量。全局名称将在模块全局词典中查找;如果未找到,则从内置模块中查找全局变量。模块的名称已从 Python 2 更改为 Python 3;在 Python 2 中,它是 __builtin__
,而在 Python 3 中现在称为 builtins
。如果您将一个属性分配给内置模块,那么此后任何模块都将看到它作为可读的全局变量,除非该模块使用其自己具有相同名称的全局变量进行了遮蔽。
阅读内置模块也可能很有用。假设您想在文件的某些部分中使用 Python 3 风格的 print
函数,但其他部分仍使用 print
语句。在 Python 2.6-2.7 中,您可以使用以下方法获取 Python 3 的 print
函数:
import __builtin__
print3 = __builtin__.__dict__['print']
from __future__ import print_function
实际上在 Python 2 中并没有导入
print
函数 - 它只是禁用了当前模块的解析规则,处理
print
就像处理其他变量标识符一样,并因此允许在内置函数中查找
print
函数。