因此,Python将始终将每个函数中的每个名称分类为本地名称,非本地名称或全局名称.这些名称范围是独占的;在每个函数内(嵌套函数中的名称都有自己的命名范围),每个名称只能属于这些类别中的一个.
当Python编译这段代码时:
def fun():
if False:
x=3
它将产生一个抽象语法树,如:
FunctionDef(
name='fun',
args=arguments(...), b
body=[
If(test=NameConstant(value=False),
body=[
Assign(targets=[Name(id='x', ctx=Store())], value=Num(n=3))
],
orelse=[])
]
)
(为简洁起见省略了一些东西).现在,当这个抽象语法树被编译成代码时,Python将扫描所有名称节点.如果存在任何名称节点,使用ctx = Store(),则该名称被认为是封闭的FunctionDef的本地名称(如果有),除非在同一函数定义中覆盖全局(即全局x)或非本地(非本地x)语句.
ctx = Store()将主要发生在赋值的左侧使用有问题的名称,或者作为for循环中的迭代变量时.
现在,当Python将其编译为字节码时,生成的字节码就是
>>> dis.dis(fun)
4 0 LOAD_GLOBAL 0 (print)
3 LOAD_FAST 0 (x)
6 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
9 POP_TOP
10 LOAD_CONST 0 (None)
13 RETURN_VALUE
优化器完全删除了if语句;但是由于变量已经标记为函数的本地变量,因此LOAD_FAST用于x,这将导致x从局部变量和局部变量访问.由于尚未设置x,因此抛出UnboundLocalError.另一方面,名称print从未被赋值,因此被认为是此函数中的全局名称,因此其值使用LOAD_GLOBAL加载.