所以我在玩 list
对象,发现如果 list
是用 list()
创建的,它会占用更多内存,这有点奇怪,比列表理解?我正在使用 Python 3.5.2
In [1]: import sys
In [2]: a = list(range(100))
In [3]: sys.getsizeof(a)
Out[3]: 1008
In [4]: b = [i for i in range(100)]
In [5]: sys.getsizeof(b)
Out[5]: 912
In [6]: type(a) == type(b)
Out[6]: True
In [7]: a == b
Out[7]: True
In [8]: sys.getsizeof(list(b))
Out[8]: 1008
来自 docs :
Lists may be constructed in several ways:
- Using a pair of square brackets to denote the empty list:
[]
- Using square brackets, separating items with commas:
[a]
,[a, b, c]
- Using a list comprehension:
[x for x in iterable]
- Using the type constructor:
list()
orlist(iterable)
但似乎使用 list()
会占用更多内存。
list
越大,差距越大。
为什么会这样?
更新 #1
使用 Python 3.6.0b2 进行测试:
Python 3.6.0b2 (default, Oct 11 2016, 11:52:53)
[GCC 5.4.0 20160609] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import sys
>>> sys.getsizeof(list(range(100)))
1008
>>> sys.getsizeof([i for i in range(100)])
912
更新 #2
使用 Python 2.7.12 进行测试:
Python 2.7.12 (default, Jul 1 2016, 15:12:24)
[GCC 5.4.0 20160609] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import sys
>>> sys.getsizeof(list(xrange(100)))
1016
>>> sys.getsizeof([i for i in xrange(100)])
920
最佳答案
我认为您看到的是过度分配模式,这是 sample from the source :
/* This over-allocates proportional to the list size, making room
* for additional growth. The over-allocation is mild, but is
* enough to give linear-time amortized behavior over a long
* sequence of appends() in the presence of a poorly-performing
* system realloc().
* The growth pattern is: 0, 4, 8, 16, 25, 35, 46, 58, 72, 88, ...
*/
new_allocated = (newsize >> 3) + (newsize < 9 ? 3 : 6);
打印长度为 0-88 的列表推导的大小,您可以看到模式匹配:
# create comprehensions for sizes 0-88
comprehensions = [sys.getsizeof([1 for _ in range(l)]) for l in range(90)]
# only take those that resulted in growth compared to previous length
steps = zip(comprehensions, comprehensions[1:])
growths = [x for x in list(enumerate(steps)) if x[1][0] != x[1][1]]
# print the results:
for growth in growths:
print(growth)
结果(格式为(列表长度,(旧总大小,新总大小))
):
(0, (64, 96))
(4, (96, 128))
(8, (128, 192))
(16, (192, 264))
(25, (264, 344))
(35, (344, 432))
(46, (432, 528))
(58, (528, 640))
(72, (640, 768))
(88, (768, 912))
过度分配是出于性能原因,允许列表增长,而无需为每次增长分配更多内存(更好的 amortized 性能)。
与使用列表推导不同的一个可能原因是,列表推导不能确定性地计算生成列表的大小,但 list()
可以。这意味着推导会在使用过度分配填充列表时不断增长列表,直到最终填充它。
一旦完成,它可能不会增加未使用分配节点的过度分配缓冲区(事实上,在大多数情况下不会,这会破坏过度分配的目的)。
list()
但是,无论列表大小如何,都可以添加一些缓冲区,因为它提前知道最终列表大小。
另一个支持证据,同样来自来源,是我们看到 list comprehensions invoking LIST_APPEND
,这表示 list.resize
的使用,这反过来表示在不知道将填充多少的情况下消耗预分配缓冲区。这与您看到的行为一致。
总而言之,list()
将根据列表大小预先分配更多节点
>>> sys.getsizeof(list([1,2,3]))
60
>>> sys.getsizeof(list([1,2,3,4]))
64
列表推导不知道列表的大小,因此它在增长时使用追加操作,从而耗尽预分配缓冲区:
# one item before filling pre-allocation buffer completely
>>> sys.getsizeof([i for i in [1,2,3]])
52
# fills pre-allocation buffer completely
# note that size did not change, we still have buffered unused nodes
>>> sys.getsizeof([i for i in [1,2,3,4]])
52
# grows pre-allocation buffer
>>> sys.getsizeof([i for i in [1,2,3,4,5]])
68
关于python - list() 使用比列表理解稍多的内存,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40018398/