什么时候检查非局部变量的存在? [英] When is the existence of nonlocal variables checked?

查看:55
本文介绍了什么时候检查非局部变量的存在?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在学习 Python,现在我正在讨论范围和非本地语句的主题.在某个时候,我以为我想通了,但后来非本地人来了,把一切都搞砸了.

I am learning Python and right now I am on the topic of scopes and nonlocal statement. At some point I thought I figured it all out, but then nonlocal came and broke everything down.

示例 1:

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

a()

自然运行会失败.
更有趣的是 print() 不会被执行.为什么?.

Running it naturally fails.
What is more interesting is that print() does not get executed. Why?.

我的理解是,在执行 print() 之前不会执行封闭的 def a(),嵌套的 def b() 是仅在调用 a() 时执行.我很困惑...

My understanding was that enclosing def a() is not executed until print() is executed, and nested def b() is executed only when a() is called. I am confused...

好的,让我们试试示例 2:

Ok, let's try example number 2:

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

a()

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

Aaand... it runs fine. Whaaat?! How did THAT fix it? x = 10 in function a is never executed!

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

My understanding was that nonlocal statement is evaluated and executed at run-time, searching enclosing function's call contexts and binding local name x to some particular "outer" x. And if there is no x in outer functions - raise an exception. Again, at run-time.

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

But now it looks like this is done at the time of syntax analysis, with pretty dumb check "look in outer functions for x = blah, if there is something like this - we're fine," even if that x = blah is never executed...

谁能解释一下非本地语句的处理时间和方式?

Can anybody explain me when and how nonlocal statement is processed?

推荐答案

a<的作用域可以看出b的作用域对自由变量(可用于绑定)了解多少/code>,像这样:

You can see what the scope of b knows about free variables (available for binding) from the scope of a, like so:

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 行,并删除带有 xif 语句,您将看到可用的自由变量b 就是 ().

If you comment out the nonlocal line, and remove the if statement with x inside, the you'll see the free variables available to b is just ().

那么让我们看看这产生了什么字节码指令,把a的定义放到IPython中,然后使用dis.dis:

So let's look at what bytecode instruction this generates, by putting the definition of a into IPython and then using 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

那么接下来我们来看看如何CLLOAD_<>/code> 在 ceval.c 中处理.

So then let's look at how LOAD_CLOSURE is processed in ceval.c.

TARGET(LOAD_CLOSURE) {
    PyObject *cell = freevars[oparg];
    Py_INCREF(cell);
    PUSH(cell);
    DISPATCH();
}

所以我们看到它必须从封闭作用域的freevars 中查找x.

So we see it must look up x from freevars of the enclosing scope(s).

在执行模型文档中提到了这一点,它说:

This is mentioned in the Execution Model documentation, where it says:

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

The nonlocal statement causes corresponding names to refer to previously bound variables in the nearest enclosing function scope. SyntaxError is raised at compile time if the given name does not exist in any enclosing function scope.

这篇关于什么时候检查非局部变量的存在?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆