python - 函数结果与可变默认参数的连接
问题描述
假设一个具有可变默认参数的函数:
def f(l=[]):
l.append(len(l))
return l
如果我运行这个:
def f(l=[]):
l.append(len(l))
return l
print(f()+["-"]+f()+["-"]+f()) # -> [0, '-', 0, 1, '-', 0, 1, 2]
或这个:
def f(l=[]):
l.append(len(l))
return l
print(f()+f()+f()) # -> [0, 1, 0, 1, 0, 1, 2]
而不是以下更合乎逻辑的:
print(f()+f()+f()) # -> [0, 0, 1, 0, 1, 2]
为什么?
解决方案
这实际上很有趣!
我们知道,函数定义中的列表l
只在该函数的定义处被初始化一次,并且对于该函数的所有调用,该列表将恰好有一份副本。现在,该函数修改了这个列表,这意味着对该函数的多次调用将多次修改完全相同的对象。这是第一个重要的部分。
现在,考虑添加这些列表的表达式:
f()+f()+f()
根据运算符优先法则,这等价于以下内容:
(f() + f()) + f()
...与此完全相同:
temp1 = f() + f() # (1)
temp2 = temp1 + f() # (2)
这是第二个重要的部分。
添加列表会生成一个新对象,而无需修改其任何参数。这是第三个重要部分。
现在让我们将我们所知道的结合在一起。
在上面的第 1 行中,第一个调用返回[0]
,正如您所期望的那样。[0, 1]
如您所料,第二个调用返回。等一下!修改后,该函数将一遍又一遍地返回完全相同的对象(不是它的副本!)!这意味着第一个调用返回的对象现在也变成了[0, 1]
!这就是为什么temp1 == [0, 1] + [0, 1]
。
然而,加法的结果是一个全新的对象,因此[0, 1, 0, 1] + f()
与 相同[0, 1, 0, 1] + [0, 1, 2]
。请注意,第二个列表也是您希望函数返回的内容。当您添加时会发生同样的事情f() + ["-"]
:这会创建一个新 list
对象,因此任何其他调用f
都不会干扰它。
您可以通过连接两个函数调用的结果来重现这一点:
>>> f() + f()
[0, 1, 0, 1]
>>> f() + f()
[0, 1, 2, 3, 0, 1, 2, 3]
>>> f() + f()
[0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5]
同样,您可以这样做,因为您正在连接对同一 object 的引用。
推荐阅读
- pandas - Add multiple rows AND single column to Dataframe based on existing column
- java - 如何实现“BottomNavigationView”但在文本旁边使用图标而不是堆叠?
- .net-core - ScheduleWidget.Core 每两周一次
- python - 无法将下载的文件存储在其相关文件夹中
- java - Android:如何检查应用程序是否正在运行
- android - 回收站视图关闭屏幕子视图持有者返回 null
- angular - 服务工作者:Angular 7 上对 API 的 HTTP GET 请求永远不会触发 fetch 事件侦听器
- python - 从numpy中的2d功率谱密度函数中检索完整的2d自相关函数
- c - C语言串口:读取非规范模式
- java - Java如何划分非常大的数字