python - 是什么导致 [*a] 过度分配?
问题描述
显然list(a)
没有过度分配,[x for x in a]
在某些时候[*a]
过度分配,并且一直过度分配?
以下是从 0 到 12 的大小 n 以及三种方法的结果大小(以字节为单位):
0 56 56 56
1 64 88 88
2 72 88 96
3 80 88 104
4 88 88 112
5 96 120 120
6 104 120 128
7 112 120 136
8 120 120 152
9 128 184 184
10 136 184 192
11 144 184 200
12 152 184 208
像这样计算,可在 repl.it 重现,使用 Python 3. 8:
from sys import getsizeof
for n in range(13):
a = [None] * n
print(n, getsizeof(list(a)),
getsizeof([x for x in a]),
getsizeof([*a]))
那么:这是如何工作的?如何[*a]
过度分配?实际上,它使用什么机制从给定的输入创建结果列表?它是否使用迭代器a
并使用类似的东西list.append
?源代码在哪里?
(Colab 使用生成图像的数据和代码。)
放大到更小的 n:
缩小到更大的 n:
解决方案
[*a]
在内部执行 C 等效项:
- 做一个新的,空的
list
- 称呼
newlist.extend(a)
- 退货
list
。
因此,如果您将测试扩展到:
from sys import getsizeof
for n in range(13):
a = [None] * n
l = []
l.extend(a)
print(n, getsizeof(list(a)),
getsizeof([x for x in a]),
getsizeof([*a]),
getsizeof(l))
你会看到 和 的结果getsizeof([*a])
是l = []; l.extend(a); getsizeof(l)
一样的。
这通常是正确的做法;当extend
您通常希望稍后添加更多内容时,对于通用解包类似,假设多个内容将一个接一个地添加。[*a]
不是正常情况;Python 假设有多个项目或可迭代对象被添加到list
( [*a, b, c, *d]
) 中,因此过度分配可以节省常见情况下的工作。
相比之下,list
由单个、预先确定大小的可迭代对象 (with list()
) 构造的 a 在使用过程中可能不会增长或缩小,并且过度分配还为时过早,除非另有证明;Python 最近修复了一个错误,该错误使构造函数即使对于已知大小的输入也会过度分配。
至于list
推导,它们实际上等同于重复append
的 s,因此您在一次添加一个元素时会看到正常过度分配增长模式的最终结果。
需要明确的是,这些都不是语言保证。这就是 CPython 实现它的方式。Python 语言规范通常不关心特定的增长模式list
(除了从最后保证摊销O(1)
append
s 和pop
s )。如评论中所述,具体实现在 3.9 中再次更改;虽然它不会影响[*a]
,但它可能会影响其他情况,以前“构建tuple
单个项目的临时,然后extend
使用tuple
”现在变成 的多个应用程序LIST_APPEND
,当发生过度分配以及计算中的数字时,这可能会发生变化。
推荐阅读
- android - 如何更改 AdMob 货币
- python - 将 Lightstreamer 与 IG Index API 一起使用
- python - 为什么我在输出 GAN 模型上得到这些线
- node.js - MongoDB Atlas 连接在 Heroku for NodeJS 上不起作用
- reactjs - 带有graphql-request的SWR如何在swr中添加变量?
- java - Swagger 没有显示我的控制器。试过邮递员,我的端点仍然失败
- java - Oracle jpa 无法插入空值
- php - wordpress 管理面板,如何防止用户删除类别?
- javascript - 尽量减少浏览器刷新的网页设计实践
- c# - EF Core DbSet AddAsync 方法:是否应该在 100% 的情况下使用异步方法?