何时检查是否存在非局部变量?

Vas*_*nov 10 python scope python-3.x

我正在学习Python,现在我正在讨论范围和非本地语句.在某些时候,我以为我想到了这一切,但随后非本地人来了,打破了一切.

示例1:

print( "let's begin" )
def a():
    def b():
        nonlocal x
        x = 20
    b()

a()
Run Code Online (Sandbox Code Playgroud)

自然运行失败.
更有趣的是,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()
Run Code Online (Sandbox Code Playgroud)

Aaand ......它运行良好.Whaaat?这是怎么解决的?x = 10在函数a中永远不会执行!

我的理解是在运行时评估和执行非本地语句,搜索封闭函数的调用上下文并将本地名称绑定x到某个特定的"外部" x.如果x外部函数中没有- 引发异常.再次,在运行时.

但现在看来这是在语法分析时完成的,非常愚蠢的检查"看看外部函数x = blah,如果有这样的东西 - 我们很好",即使x = blah从未执行过......

任何人都可以解释我何时以及如何处理非本地语句?

ely*_*ely 4

您可以从 的作用域中了解 的作用域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()
Run Code Online (Sandbox Code Playgroud)

这使:

let's begin
('x',)
Run Code Online (Sandbox Code Playgroud)

如果您注释掉该nonlocal行,并删除内部的if语句x,您将看到可用的自由变量b只是()

因此,让我们看看它会生成什么字节码指令,将 的定义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
Run Code Online (Sandbox Code Playgroud)

那么接下来我们看看中是如何LOAD_CLOSUREceval.c处理的。

TARGET(LOAD_CLOSURE) {
    PyObject *cell = freevars[oparg];
    Py_INCREF(cell);
    PUSH(cell);
    DISPATCH();
}
Run Code Online (Sandbox Code Playgroud)

x所以我们看到它必须从freevars封闭的范围中查找。

执行模型文档中提到了这一点,其中写道:

非局部语句导致相应的名称引用最近的封闭函数范围中先前绑定的变量。如果给定名称不存在于任何封闭函数作用域中,则会在编译时引发 SyntaxError 。