何时检查非局部变量的存在?
我正在学习Python,现在我正在讨论范围和非本地语句。在某个时候,我以为我想通了一切,但是后来非本地人来了,把一切都分解了。
示例1:
print( "let's begin" )
def a():
def b():
nonlocal x
x = 20
b()
a()
运行它自然会失败。
更有趣的是print(
)没有执行。为什么?。
我的理解是,封闭def a()
不执行之前print()
执行,并嵌套def b()
时,才会执行a()
被调用。我很迷惑…
好,让我们尝试示例2:
print( "let's begin" )
def a():
if False: x = 10
def b():
nonlocal x
x = 20
b()
a()
Aaand …运行良好。哇!!它是如何解决的?x = 10
功能a
永远不会执行!
我的理解是,非本地语句是在运行时评估和执行的,它搜索封闭函数的调用上下文,并将本地名称绑定x
到某些特定的“外部” x
。如果x
外部函数没有-
引发异常。同样,在运行时。
但是现在看来,这似乎是在语法分析时完成的,只需进行愚蠢的检查,“检查外部函数x = blah
是否存在,如果这样的话-我们很好,”即使x =
blah
从未执行过…
谁能解释我何时以及如何处理非本地语句?
-
您可以从的范围中看到
b
有关自由变量(可用于绑定)的了解范围a
,如下所示:import inspect print( "let's begin" ) def a(): if False: x = 10 def b(): print(inspect.currentframe().f_code.co_freevars) nonlocal x x = 20 b() a()
这使:
let's begin ('x',)
如果您注释掉该
nonlocal
行,并删除其中的if
语句,x
则会看到可供使用的自由变量b
is()
。因此,让我们看一下它生成的字节码指令,方法是将的定义
a
放入IPython中,然后使用dis.dis
:In [3]: import dis In [4]: dis.dis(a) 5 0 LOAD_CLOSURE 0 (x) 2 BUILD_TUPLE 1 4 LOAD_CONST 1 (<code object b at 0x7efceaa256f0, file "<ipython-input-1-20ba94fb8214>", line 5>) 6 LOAD_CONST 2 ('a.<locals>.b') 8 MAKE_FUNCTION 8 10 STORE_FAST 0 (b) 10 12 LOAD_FAST 0 (b) 14 CALL_FUNCTION 0 16 POP_TOP 18 LOAD_CONST 0 (None) 20 RETURN_VALUE
因此,让我们看一下如何
LOAD_CLOSURE
处理ceval.c
。TARGET(LOAD_CLOSURE) { PyObject *cell = freevars[oparg]; Py_INCREF(cell); PUSH(cell); DISPATCH(); }
因此,我们看到它必须
x
从freevars
封闭的范围中查找。nonlocal语句使对应的名称引用最近的封闭函数范围中的先前绑定的变量。如果给定名称在任何封闭函数范围中不存在,则 在编译时
引发SyntaxError 。