在构建参数化装饰器时,我没有意识到在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实际上在做什么?
答案 0 :(得分:2)
如果函数包含对变量的赋值(包括-=
等扩充赋值,则该变量自动为本地变量,除非明确声明为global
(或nonlocal
)。如果有没有任何赋值,它是自动全局的,不需要任何声明(因为当它没有任何值的源时它很难成为局部变量)。这个分析是在生成任何代码之前执行的,所以你得到这样的情况后续的代码行可能导致较早的行成为错误。