什么时候检查非局部变量的存在?

2024-09-30 05:32:26 发布

您现在位置:Python中文网/ 问答频道 /正文

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

示例1:

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

a()

运行它自然会失败。
更有趣的是,print()没有被执行。为什么?在

我的理解是,封闭def a()直到print()才执行,而嵌套的{}只在调用a()时执行。我很困惑。。。在

好的,让我们来看看例子2:

^{pr2}$

啊还有。。。它运行良好。 什么?!那是怎么解决的?^函数a中的{}从不执行!在

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

但现在看来这是在语法分析时完成的,使用相当愚蠢的检查“lookinouterfunctions for x = blah,如果有类似的东西-我们很好”,即使{}从未执行过。。。在

有谁能解释一下非本地语句是何时以及如何处理的吗?在


Tags: 函数名称示例主题def语句例子print
2条回答

首先,要明白python将检查模块的语法,如果它检测到无效的内容,它会发出一个SyntaxError,这会完全停止它的运行。您的第一个例子提出了一个SyntaxError,但是要确切地理解原因是相当复杂的,尽管如果您知道__slots__是如何工作的,那么就更容易理解了,所以我将首先快速介绍这一点。在


当一个类定义__slots__时,它基本上是说实例应该只具有那些属性,这样每个对象都被分配了内存,只为这些属性分配空间,试图分配其他属性会引发错误

class SlotsTest:
    __slots__ = ["a", "b"]

x = SlotsTest()

x.a = 1 ; x.b = 2
x.c = 3 #AttributeError: 'SlotsTest' object has no attribute 'c'

x.c = 3不能工作的原因是没有内存空间来放置.c属性。在

如果不指定__slots__,则所有实例都是用字典创建的,以存储实例变量,字典对它们包含的值没有任何限制

^{pr2}$

Python函数的工作原理类似于slots。当python检查模块的语法时,它会在每个函数定义中找到分配(或试图分配)的所有变量,并在执行期间构造框架时使用这些变量。在

当您使用nonlocal x时,它给了一个内部函数访问外部函数作用域中的特定变量,但是如果外部函数中没有定义变量,则nonlocal x就没有空间可指向。

全局访问不会遇到相同的问题,因为python模块是用字典来存储其属性的。因此,即使没有对x的全局引用,也允许global x

您可以从a的作用域中了解b对自由变量(可用于绑定)的了解,如下所示:

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()

它给出了:

^{pr2}$

如果您注释掉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

那么让我们看看how ^{} is processed in ^{}。在

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

所以我们看到它必须从封闭作用域的x查找x。在

这里提到的是in the Execution Model documentation,它说:

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.

相关问题 更多 >

    热门问题