Python局部变量编译原理
问题内容:
def fun():
if False:
x=3
print(locals())
print(x)
fun()
输出和错误消息:
{}
---------------------------------------------------------------------------
UnboundLocalError Traceback (most recent call last)
<ipython-input-57-d9deb3063ae1> in <module>()
4 print(locals())
5 print(x)
----> 6 fun()
<ipython-input-57-d9deb3063ae1> in fun()
3 x=3
4 print(locals())
----> 5 print(x)
6 fun()
UnboundLocalError: local variable 'x' referenced before assignment
我想知道python解释器如何工作。请注意,x =
3根本不会运行,并且不应将其视为局部变量,这意味着错误将是“未定义名称’x’”。但是,查看代码和错误消息并非如此。任何人都可以在这种情况下解释python解释器的编译机制原理吗?
问题答案:
因此,Python始终会将每个函数中的每个名称归类为 local , non-local 或 global之一
。这些名称范围是排他的;在每个函数中(嵌套函数中的名称都有其自己的命名范围),每个名称只能属于这些类别之一。
当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将扫描所有名称节点。如果存在Name
带有的节点,则ctx=Store()
该名称被认为是封闭的
本地 名称FunctionDef
,除非在同一函数定义中用global
(即global x
)或nonlocal
(nonlocal 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
。