我翻阅了Fluent Python一书。它指出,对于所有数字的序列,数组比List更有效和更快。从我收集到的内容来看,它也减少了内存开销。它表明" Python数组像C数组一样精简。"
我很好奇为什么这里的数组显示的内存比列表多。
import array
from random import random
import sys
floats = array.array('d', (random() for i in range(10**7)))
L = [random() for i in range(10**7)]
print(sys.getsizeof(floats))
print(sys.getsizeof(L))
输出
81940352
81528056
答案 0 :(得分:3)
你选择了错误的例子。使用array
的关键是当您需要存储其本机表示小于Python对象引用的项目时。 (这里似乎是8个字节。)例如如果你这样做:
from array import array
from os import urandom
a = array('B', urandom(1024))
l = list(a)
sys.getsizeof(a) # => 1155
sys.getsizeof(l) # => 9328
由于double
s也是8字节宽,因此存储它们的方式确实不比8字节更紧凑。
至于本书中的其他主张,它们带有一些盐 - 你无法运行Python代码 - 也就是说,操作由Python解释器执行 - 并且与C一样快。你'在将Python对象写入数组或从数组中读取它们时仍然会产生开销,在本机函数中对整个数组执行某种大操作会更快。
答案 1 :(得分:2)
很抱歉,但我不认为@millimoose的答案解释得很清楚,当他说阵列比列表更快时,真正发生了什么或者作者是什么意思。
内存占用
double需要8个字节,如果将它存储在array
中,则需要多少内存 - 它不是存储为Python-Float,而是存储为原始的8字节值。然而,由于过度分配和数组对象中保存的一些额外数据(数组大小,缓冲区大小,数组中值的类型等),开销很小。
Python-Float需要超过8个字节:
>>> import sys
>>> f=1.0
>>> sys.getsizeof(f)
24
24个字节 - 对于Python对象来说非常小!例如,通常的空Python对象需要(Python3):
>>> class A:
pass
>>> a=A()
>>> sys.getsizeof(a)
56
56个字节。有减少所需字节数的技巧,它们全部用于Python-Floats但是仍然需要8个字节用于双值,另外8个字节用于引用计数器,8个字节用于指向类型描述的指针(所以对象知道它是一个Float对象。)
此外,对象本身不是存储在列表中,而是存储在它的引用(即指针),它本身需要8个字节。因此,在列表中保存Python-float基本上需要32个字节 - 是所用内存量的4倍。
那么为什么在为列表调用sys.getsizeof
时会看到不同的内容?答案:sys.getsizeof
是not recursive:
sys.getsizeof(object [,default])
...
只考虑直接归因于的内存消耗,而不考虑它所引用的对象的内存消耗。
这意味着列表的getsizeof
仅计算引用Float对象所需的内存(每个引用8个字节),而不是对象的大小。为了说明这一点:
>>> lst=[list(range(1000))]
>>> sys.getsizeof(lst)
72
显然,使用的内存比报告的72字节多。
要查看实际内存消耗,您需要考虑解释器所需的内存:
>>> /usr/bin/time -fpeak_used_memory:%M python -c "l=list((float(i) for i in range(10**7)))"
peak_used_memory:326832
>>> /usr/bin/time -fpeak_used_memory:%M python -c "import array; a=array.array('d',(float(i) for i in range(10**7)))"
peak_used_memory:88076
我们可以看到差异(320 MB对80MB)与预期因子4有关。
<强>速度强>
作者没有说,使用array.array
和python-interpreter会让你加快速度。相反,使用array.array
和python-operations会使速度变慢,因为首先必须将原始double值转换为Python-Floats:
lst=list(range(10**6))
%timeit sum(lst)
7.19 ms ± 461 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
import array
a=array.array('i',range(10**6))
%timeit sum(a)
17.9 ms ± 43.7 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
慢了近3倍!
然而,有可能加快速度 - 它不是那么简单。因为那个人会使用numpy,cython或numba。例如:
import numpy as np
b=np.array(range(10**6), dtype=np.int32)
%timeit b.sum()
1.07 ms ± 24.8 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
快了近10倍!