关于Python3时间没有彻底的答案,所以我在这里做了一个回答。此处描述的大部分内容在 Python 3 文档的4.2.2 名称解析中有详细说明。
正如其他答案中所提供的,有 4 个基本范围,即 LEGB,用于本地、封闭、全局和内置。除此之外,还有一个特殊的范围,即类主体,它不包含在类中定义的方法的封闭范围;类体内的任何赋值都会使变量从那里绑定到类体内。
特别是,除了and之外,没有块语句创建变量范围。在 Python 2 中,列表推导式不会创建变量作用域,但在 Python 3 中,列表推导式中的循环变量是在新作用域中创建的。def
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)
因此与函数体不同,你可以在类体中将变量重新分配给同名的变量,以获得同名的类变量;对此名称的进一步查找将解析为类变量。
对于 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) # 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
文件说_
与 global 语句中列出的名称不同,在 nonlocal 语句中列出的名称必须引用封闭范围中的预先存在的绑定(无法明确确定应创建新绑定的范围)。
ienonlocal
总是指名称已绑定的最内层外部非全局范围(即分配给,包括用作for
目标变量、在with
子句中或作为函数参数)。
任何不被视为当前作用域或任何封闭作用域的局部变量都是全局变量。在模块全局字典中查找全局名称;如果未找到,则从内置模块中查找全局;模块名称从 python 2 更改为 python 3;在 python 2 中它是__builtin__
,在 python 3 中它现在被称为builtins
. 如果您分配给内置模块的属性,则此后任何模块都可以将其作为可读的全局变量可见,除非该模块使用其自己的同名全局变量来隐藏它们。
阅读内置模块也很有用;假设您希望文件的某些部分使用 python 3 样式的打印功能,但文件的其他部分仍然使用该print
语句。在 Python 2.6-2.7 中,您可以通过以下方式获取 Python 3print
函数:
import __builtin__
print3 = __builtin__.__dict__['print']
from __future__ import print_function
实际上并没有在 Python 2 中的任何地方导入该函数- 相反,它只是禁用当前模块print
中语句的解析规则,像任何其他变量标识符一样处理,从而允许在内置函数中查找该函数。print
print
print