我做了一个实验,研究每种Python数组类型消耗多少内存,分别是list
,tuple
,set
,dict
,np.array
。然后我得到了以下结果。
(x轴是数组的长度,y轴是内存大小。)
我发现Python set
花费的内存量逐步增加(也dict
),而其他的则按我的预期线性增加。我想知道是什么让他们与众不同。
我使用了以下get_size()
函数。 (reference)
def get_size(obj, seen = None):
size = sys.getsizeof(obj)
if seen is None:
seen = set()
obj_id = id(obj)
if obj_id in seen:
return 0
seen.add(obj_id)
if isinstance(obj, dict):
size += sum([get_size(v, seen) for v in obj.values()])
size += sum([get_size(k, seen) for k in obj.keys()])
elif hasattr(obj, '__dict__'):
size += get_size(obj.__dict__, seen)
elif hasattr(obj, '__iter__') and not isinstance(obj, (str, bytes, bytearray)):
size += sum([get_size(i, seen) for i in obj])
return size
然后我以100个间隔测量了从0到10,000长度的内存。
答案 0 :(得分:3)
CPython集合和字典始终使用2的幂的内部哈希表。 list
,tuple
和numpy.ndarray
在其基础内存缓冲区的大小方面都具有更大的灵活性,但是set
和dict
被硬编码为使用强大的功能-两种尺寸的桌子。该实现无法以非2的幂的表大小运行。参见Objects/dictobject.c
和Objects/setobject.c
。
图表中的跳转是表大小跳转到新的2的幂时。
偶然地,您的get_size
不能很好地工作。例如,它有两个影响numpy.ndarray
情况的错误,这些错误几乎被抵消了(但不完全相同)。它尝试将NumPy数组的元素大小添加到整个数组的大小,但是对于NumPy数组,元素的大小已经由getsizeof
所占。另外,它使用id
确定对象的身份,但是通过迭代NumPy数组而生成的对象是即时创建并立即死亡的,因此它们的id
值不是唯一的。在实践中,这可能会使大小超出代表数组元素的对象大小的一到两倍。