嵌套函数中的变量引用和赋值顺序

时间:2017-09-05 16:48:26

标签: python python-3.x scope

来自词法范围的Google Style Guide

  

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

此规范可在此处查看:

def toplevel():
    a = 5
    def nested():
        # Tries to print local variable `a`, but `a` is created locally after,
        # so `a` is referenced before assignment.  You would need `nonlocal a`
        print(a + 2)
        a = 7
    nested()
    return a
toplevel()
# UnboundLocalError: local variable 'a' referenced before assignment

颠倒nested中两个语句的顺序可以解决这个问题:

def toplevel():
    a = 5
    def nested():
        # Two statements' order reversed, `a` is now locally assigned and can
        # be referenced
        a = 7
        print(a + 2)
    nested()
    return a
toplevel()

我的问题是, Python的实现是什么告诉第一个函数a将在本地声明(在print语句之后)?我的理解是Python有效地逐行解释。那么,它不应该默认在代码中的那个点寻找非本地a吗?

详细说明,如果我使用只是参考(没有作业),

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

以某种方式,print语句知道引用封闭函数中定义的非局部a。但是,如果我在该行之后分配给本地a ,那么该功能对于自己的好处来说太聪明了。

3 个答案:

答案 0 :(得分:1)

  

我的理解是Python有效地逐行解释

那你错了。在任何解释开始之前,整个文件被编译为字节码。

此外,即使字节码编译通过不存在,print(a + 2)实际上也不会在看到a = 7之前执行,因为它在函数定义中。在实际尝试执行a = 7时,Python仍然会知道print(a + 2)

答案 1 :(得分:1)

  

我的理解是Python有效地逐行解释。

那不是正确的心理模型。

分析整个函数的主体以确定哪些名称引用局部变量而哪些名称不引用。

为了简化您的示例,以下内容还提供了UnboundLocalError

def func():
  print(a)
  a = 2

func()

这里,func()编译为以下字节码:

  2           0 LOAD_FAST                0 (a)
              3 PRINT_ITEM
              4 PRINT_NEWLINE

  3           5 LOAD_CONST               1 (2)
              8 STORE_FAST               0 (a)
             11 LOAD_CONST               0 (None)
             14 RETURN_VALUE

将此与

进行比较
def gunc():
  print(a)

编译为

  2           0 LOAD_GLOBAL              0 (a)
              3 PRINT_ITEM
              4 PRINT_NEWLINE
              5 LOAD_CONST               0 (None)
              8 RETURN_VALUE

观察a的分配缺失如何将引用从本地变为全局。

答案 2 :(得分:0)

根据document

  

Python的一个特殊之处在于 - 如果没有global语句生效 - 对名称的赋值总是进入最里面的范围。分配不复制数据 - 它们只是将名称绑定到对象。