Python中一个包含一百万个元素的列表需要多少内存?

17
根据redditmetrics.com,Reddit上有超过一百万个subreddit。
我编写了一个脚本,重复查询this Reddit API endpoint,直到所有的subreddit都被存储在一个数组all_subs中:
all_subs = []
for sub in <repeated request here>:
    all_subs.append({"name": display_name, "subscribers": subscriber_count})

脚本已运行近十小时,目前完成了一半(每三到四个请求会被限速)。完成后,我期望得到如下的数组:
[
    { "name": "AskReddit", "subscribers", 16751677 },
    { "name": "news", "subscribers", 13860169 },
    { "name": "politics", "subscribers", 3350326 },
    ... # plus one million more entries
]

大约需要多少内存空间来存储这个列表?

2
那不是一个数组,那是一个列表 - juanpa.arrivillaga
2
使用哪个版本的Python?CPython(即来自python.org的“标准”实现)?是2还是3的版本? - Chris
2
还有,Python的32位或64位版本? - Tim Peters
@juanpa.arrivillaga 已经修复了 :-) 它是 Python 的 64 位版本 2。 - Lincoln Bergeson
可能是如何确定Python中对象的大小?的重复问题。 - aghast
1个回答

33

这取决于你的Python版本和系统,但我会帮助你计算大概需要多少内存。首先,sys.getsizeof只返回表示容器的对象的内存使用情况,而不是容器中所有元素的内存使用情况。

只有直接与该对象相关的内存消耗被计入,而非它所引用的对象的内存消耗。

如果指定了默认值,在对象没有提供检索大小的手段时将返回默认值。否则,将引发TypeError。

getsizeof()调用对象的__sizeof__方法,并在对象由垃圾收集器管理时添加额外的垃圾收集器开销。

请参见递归大小配方,其中使用getsizeof()递归地查找容器及其所有内容的大小的示例。

因此,我在交互式解释器会话中加载了那个配方:

所以,CPython list实际上是一个异构可调整大小的数组列表。底层数组仅包含指向Py_Objects的指针。因此,一个指针占用一个机器字的内存。在64位系统上,这是64位,即8个字节。因此,仅对于容器,大小为1,000,000的列表大约需要占用8百万字节,或8兆字节。构建一个具有1000000个条目的列表可以证明这一点:

In [6]: for i in range(1000000):
   ...:     x.append([])
   ...:

In [7]: import sys

In [8]: sys.getsizeof(x)
Out[8]: 8697464

额外的内存消耗可以归因于 Python 对象的开销,以及底层数组为实现高效的 .append 操作而留下的额外空间。

现在,在 Python 中,字典是相当重量级的。仅仅容器本身:

In [10]: sys.getsizeof({})
Out[10]: 288

因此,100万个字典的大小的下限为:288000000字节。因此,一个粗略的下限:

In [12]: 1000000*288 + 1000000*8
Out[12]: 296000000

In [13]: 296000000 * 1e-9 # gigabytes
Out[13]: 0.29600000000000004

因此,您可以期望大约0.3GB的内存。使用该处方和更实际的dict

In [16]: x = []
    ...: for i in range(1000000):
    ...:     x.append(dict(name="my name is what", subscribers=23456644))
    ...:

In [17]: total_size(x)
Out[17]: 296697669

In [18]:

那么,大约是0.3 GB。在现代系统中这并不多。但是如果你想要节省空间,你应该使用一个 tuple ,甚至更好的是使用一个 namedtuple

In [24]: from collections import namedtuple

In [25]: Record = namedtuple('Record', "name subscribers")

In [26]: x = []
    ...: for i in range(1000000):
    ...:     x.append(Record(name="my name is what", subscribers=23456644))
    ...:

In [27]: total_size(x)
Out[27]: 72697556

或者,以千兆字节计:

In [29]: total_size(x)*1e-9
Out[29]: 0.07269755600000001

namedtuple 的使用方式与 tuple 相同,但可以使用名称访问字段:

In [30]: r = x[0]

In [31]: r.name
Out[31]: 'my name is what'

In [32]: r.subscribers
Out[32]: 23456644

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接