【问题标题】:Accessing variables defined in enclosing scope访问在封闭范围中定义的变量
【发布时间】:2018-02-11 14:50:55
【问题描述】:

来自Google Style Guide 的词法作用域:

嵌套的 Python 函数可以引用在封闭中定义的变量 功能,但不能分配给它们。

这两个似乎一开始都检查出来了:

# Reference
def toplevel():
    a = 5
    def nested():
        print(a + 2)
    nested()
    return a
toplevel()
7
Out[]: 5

# Assignment
def toplevel():
    a = 5
    def nested():
        a = 7 # a is still 5, can't modify enclosing scope variable
    nested()
    return a
toplevel()
Out[]: 5

那么,为什么嵌套函数中同时引用和赋值会导致异常呢?

# Reference and assignment
def toplevel():
    a = 5
    def nested():
        print(a + 2)
        a = 7
    nested()
    return a
toplevel()
# UnboundLocalError: local variable 'a' referenced before assignment

【问题讨论】:

  • 请注意,print(a+2);a=7 组合不起作用,但是,a=7;print(a+2) 组合有效。

标签: python python-3.x


【解决方案1】:

对于遇到此问题的任何人,除了此处接受的答案外,Python docs 中也有简洁的回答:

这段代码:

>>> x = 10
>>> def bar():
...     print(x)
>>> bar()
10

有效,但是这段代码:

>>> x = 10
>>> def foo():
...     print(x)
...     x += 1

导致UnboundLocalError

这是因为当您对范围内的变量进行赋值时, 该变量成为该范围的本地变量并以类似方式隐藏任何变量 外部范围内的命名变量。自 foo 中的最后一条语句 为x 分配一个新值,编译器将其识别为本地 多变的。因此,当较早的 print(x) 尝试打印 未初始化的局部变量和错误结果。

在上面的示例中,您可以通过以下方式访问外部范围变量 声明它global:

>>> x = 10
>>> def foobar():
...     global x
...     print(x)
...     x += 1
>>> foobar()
10

您可以使用nonlocal 在嵌套范围内做类似的事情 关键词:

>>> def foo():
...    x = 10
...    def bar():
...        nonlocal x
...        print(x)
...        x += 1
...    bar()
...    print(x)
>>> foo()
10
11

【讨论】:

    【解决方案2】:

    在第一种情况下,您指的是 nonlocal 变量,这是可以的,因为没有名为 a 的局部变量。

    def toplevel():
        a = 5
        def nested():
            print(a + 2) # theres no local variable a so it prints the nonlocal one
        nested()
        return a
    

    在第二种情况下,您创建一个局部变量a,这也很好(局部a 将不同于非局部变量,这就是为什么原始a 没有更改)。

    def toplevel():
        a = 5 
        def nested():
            a = 7 # create a local variable called a which is different than the nonlocal one
            print(a) # prints 7
        nested()
        print(a) # prints 5
        return a
    

    在第三种情况下,您创建了一个局部变量,但在此之前您有 print(a+2),这就是引发异常的原因。因为print(a+2) 将引用在该行之后创建的局部变量a

    def toplevel():
        a = 5
        def nested():
            print(a + 2) # tries to print local variable a but its created after this line so exception is raised
            a = 7
        nested()
        return a
    toplevel()
    

    要实现你想要的,你需要在你的内部函数中使用nonlocal a

    def toplevel():
        a = 5
        def nested():
            nonlocal a
            print(a + 2)
            a = 7
        nested()
        return a
    

    【讨论】:

    • 在第三种情况下,nested如何“知道”搜索局部变量而不是使用封闭函数的a?否则我会按照你的回答,但不是这个方面,因为 python 是逐行解释的。
    • 尽管它是一种解释性语言,但在运行之前会检查整个语法。看看thisthis
    • @BradSolomon Python 的语义不依赖于它被解释。即使是 Python 的规范“解释”实现 CPython,也不会“逐行解释”。它将程序的文本编译为字节码,然后该字节码在 cpython 虚拟机 (VM) 上运行。字节码是特定“CPU”的机器代码,即 cpython VM。
    猜你喜欢
    • 2012-04-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多