是否在 Python 中的函数调用时进行分配?
Posted
技术标签:
【中文标题】是否在 Python 中的函数调用时进行分配?【英文标题】:Are assignments made at function call in Python? 【发布时间】:2017-09-21 21:28:09 【问题描述】:在构建参数化装饰器时,我没有意识到 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 建议的所需输出。
虽然问题解决了,但我想了解问题的根源。我注意到回溯指向参数化参数s
(例如print(s)
)的第一次使用,而不是指向实际导致错误的行(即while
循环/赋值)。
我怀疑在调用函数时,Python 首先建立本地范围的分配。然后赋值优先于或覆盖从外部范围继承的变量。因此,如果没有分配给s
,则使用外部s
。相比之下,通过赋值,s
在函数调用时重新定义,初始赋值之前的任何引用都会引发错误。这是正确的,还是有人可以解释Python实际上在做什么?
【问题讨论】:
也许你可以看看 Python 如何使用pythontutor.com逐行执行文件! @Windmill 我很欣赏这个建议。目前,pythontutor 显示s
在a()
的框架中可用,但并没有明确指出s
在嵌套函数中是如何观察到的。
【参考方案1】:
如果函数包含对变量的赋值(包括诸如-=
之类的扩展赋值,则该变量自动为局部变量,除非显式声明为global
(或nonlocal
)。如果没有赋值,则自动为全局的,不需要任何声明(因为当它没有值的来源时,它几乎不可能是局部变量)。这种分析是在生成任何代码之前执行的,所以你会遇到这样的情况,即后续代码行可能导致较早的行变成错误。
【讨论】:
这证实了我的怀疑。您是否知道支持这一点的参考资料?以上是关于是否在 Python 中的函数调用时进行分配?的主要内容,如果未能解决你的问题,请参考以下文章