一个包含一百万个元素的列表在 Python 中将占用多少内存?

How much memory will a list with one million elements take up in Python?

根据 redditmetrics.com

,Reddit 上有超过一百万个 subreddits

我写了一个脚本,重复查询 this Reddit API endpoint 直到所有的 subreddits 都存储在一个数组中,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
]

这个列表大约会占用多少space内存?

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

Only the memory consumption directly attributed to the object is accounted for, not the memory consumption of objects it refers to.

If given, default will be returned if the object does not provide means to retrieve the size. Otherwise a TypeError will be raised.

getsizeof() calls the object’s __sizeof__ method and adds an additional garbage collector overhead if the object is managed by the garbage collector.

See recursive sizeof recipe for an example of using getsizeof() recursively to find the size of containers and all their contents.

所以,我已经在交互式解释器会话中加载了该食谱:

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

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

In [7]: import sys

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

额外的内存由 python 对象的开销和底层数组最后留下的额外 space 来考虑,以允许高效的 .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.3 GB 的内存。使用 recipie 和更现实的 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 场演出。现在,这在现代系统上并不多。但是如果你想保存 space,你应该使用 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 一样工作,但您可以使用 names:

访问字段
In [30]: r = x[0]

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

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