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

Lin*_*son 9 python memory arrays reddit

根据redditmetrics.com的数据,Reddit上有超过一百万个子评价.

我写了一个脚本,反复查询这个Reddit API端点,直到所有的subreddit都存储在一个数组中,all_subs:

all_subs = []
for sub in <repeated request here>:
    all_subs.append({"name": display_name, "subscribers": subscriber_count})
Run Code Online (Sandbox Code Playgroud)

该脚本已经运行了近十个小时,大约已经完成了一半(每三个或四个请求就会受到速率限制).当它完成后,我期待一个像这样的数组:

[
    { "name": "AskReddit", "subscribers", 16751677 },
    { "name": "news", "subscribers", 13860169 },
    { "name": "politics", "subscribers", 3350326 },
    ... # plus one million more entries
]
Run Code Online (Sandbox Code Playgroud)

这个列表占用的内存空间大约是多少?

jua*_*aga 15

这取决于你的Python版本和你的系统,但我会帮你弄清楚它需要多少内存.首先,首先,sys.getsizeof只返回表示容器的对象的内存使用,而不是容器中的所有元素.

仅考虑直接归因于对象的内存消耗,而不考虑它所引用的对象的内存消耗.

如果给定,则如果对象未提供检索大小的方法,则将返回default.否则将引发TypeError.

getsizeof()__sizeof__如果对象由垃圾收集器管理,则调用该对象的方法并添加额外的垃圾收集器开销.

有关递归使用getsizeof() 以查找容器大小及其所有内容的示例,请参阅recursive sizeof recipe.

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

因此,CPython 列表实际上是一个异构的,可调整大小的arraylist.底层数组只包含指向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
Run Code Online (Sandbox Code Playgroud)

额外的内存由python对象的开销和底层数组在末尾留下的额外空间来计算,以实现高效的.append操作.

现在,字典在Python中相当重要.只是容器:

In [10]: sys.getsizeof({})
Out[10]: 288
Run Code Online (Sandbox Code Playgroud)

因此,100万个dicts大小的下限是:288000000字节.所以,粗略的下限:

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

In [13]: 296000000 * 1e-9 # gigabytes
Out[13]: 0.29600000000000004
Run Code Online (Sandbox Code Playgroud)

所以你可以期待大约0.3千兆字节的内存.使用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]:
Run Code Online (Sandbox Code Playgroud)

所以,大约0.3演出.现在,这在现代系统上并不是很多.但如果你想节省空间,你应该使用一个tuple甚至更好的,a 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
Run Code Online (Sandbox Code Playgroud)

或者,以千兆字节为单位:

In [29]: total_size(x)*1e-9
Out[29]: 0.07269755600000001
Run Code Online (Sandbox Code Playgroud)

namedtuple像a一样工作tuple,但你可以访问名称的字段:

In [30]: r = x[0]

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

In [32]: r.subscribers
Out[32]: 23456644
Run Code Online (Sandbox Code Playgroud)