是否在Python函数调用中进行了赋值?

时间:2017-04-25 03:14:28

标签: python function variable-assignment

在构建参数化装饰器时,我没有意识到在Python中不允许在嵌套函数中传递参数的重新分配。进一步观察,我意识到这对于简单的功能也是如此。我已将演示缩减为以下嵌套函数:

def a(s):
    def b():
        def c():
#             nonlocal s                         # fix
            print(s)
#             while s:
#                 s -= 1                         # uncommenting raises UnboundLocalError 
            print(s)
            return None
        return c()
    return b()

a(3)
# 3
# 3

我希望通过添加注释的while循环来获得以下所需的输出:

a(3)
# 3
# 0

接下来,取消注释while循环的两行会出现以下错误,这表示将值重新分配给s会引发错误:

<ipython-input-37-7141eb599936> in c()
      3         def c():
      4 #             nonlocal s                 # fix
----> 5             print(s)
      6             while s:
      7                 s -= 1                   # uncommenting raises UnboundLocalError

UnboundLocalError: local variable 's' referenced before assignment

最后,取消注释nonlocal可修复此问题,并根据this post的建议提供所需的输出。

虽然问题已经解决,但我想了解问题的根源。我注意到traceback指向第一次使用参数化参数s(例如print(s)),而不是指向实际导致错误的行(即while循环/赋值) 。

我怀疑在调用函数时,Python首先建立本地范围的赋值。然后,赋值从外部作用域获取更高的优先级或覆盖继承的变量。因此,如果没有s的分配,则使用外部s。相反,通过赋值,在函数调用中重新定义s,并且在初始赋值之前的任何引用都将引发错误。这是正确的,还是有人可以解释Python实际上在做什么?

1 个答案:

答案 0 :(得分:2)

如果函数包含对变量的赋值(包括-=等扩充赋值,则该变量自动为本地变量,除非明确声明为global(或nonlocal)。如果有没有任何赋值,它是自动全局的,不需要任何声明(因为当它没有任何值的源时它很难成为局部变量)。这个分析是在生成任何代码之前执行的,所以你得到这样的情况后续的代码行可能导致较早的行成为错误。