是否在 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 显示sa() 的框架中可用,但并没有明确指出s 在嵌套函数中是如何观察到的。 【参考方案1】:

如果函数包含对变量的赋值(包括诸如-= 之类的扩展赋值,则该变量自动为局部变量,除非显式声明为global(或nonlocal)。如果没有赋值,则自动为全局的,不需要任何声明(因为当它没有值的来源时,它几乎不可能是局部变量)。这种分析是在生成任何代码之前执行的,所以你会遇到这样的情况,即后续代码行可能导致较早的行变成错误。

【讨论】:

这证实了我的怀疑。您是否知道支持这一点的参考资料?

以上是关于是否在 Python 中的函数调用时进行分配?的主要内容,如果未能解决你的问题,请参考以下文章

Python函数进阶

python基础之函数

Python全栈之路----函数----参数

Python的函数参数

python之形参和实参

外部文件中的Python重新分配功能