我们可以花点时间找出答案:
>>> import numpy as np>>> a = np.arange(32)>>> aarray([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31])>>> a.data<read-write buffer for 0x107d01e40, size 256, offset 0 at 0x107d199b0>>>> id(a.data)4433424176>>> id(a[0])4424950096>>> id(a[1])4424950096>>> for item in a:... print id(item)... 44249500964424950120442495009644249501204424950096442495012044249500964424950120442495009644249501204424950096442495012044249500964424950120442495009644249501204424950096442495012044249500964424950120442495009644249501204424950096442495012044249500964424950120442495009644249501204424950096442495012044249500964424950120
那么这是怎么回事?首先,我看了数组的内存缓冲区的内存位置。在
4433424176。这本身并 不太有
启发性。但是,numpy将其数据存储为连续的C数组,因此numpy数组中的第一个元素 应对 应于数组本身的内存地址,但不这样:
>>> id(a[0])4424950096
但这不是一件好事,因为那会破坏python中的不变性,即2个对象
id在其生命周期内永远不会具有相同的对象。
那么,numpy如何做到这一点?好吧,答案是numpy必须将返回的对象包装为python类型(例如
numpy.float64,
numpy.int64在这种情况下为),如果您逐项迭代1,则会花费时间。迭代时将进一步证明这一点-
我们看到,在数组上进行迭代时,我们在2个独立的ID之间交替。这意味着python的内存分配器和垃圾收集器正在超时工作以创建新对象,然后释放它们。
一个 名单 没有这个内存分配器/垃圾收集开销。列表中的对象已经作为python对象存在(并且它们在迭代后仍将存在),因此在列表的迭代中都没有任何作用。
时序方法:
另请注意,您的时间安排会因您的假设而有所偏离。您以为
k + 1在两种情况下这应该花费相同的时间,但是事实并非如此。请注意,如果我重复您的时间 没有
做任何加法:
mgilson$ python -m timeit -s "import numpy" "for k in numpy.arange(5000): k"1000 loops, best of 3: 233 usec per loopmgilson$ python -m timeit "for k in range(5000): k"10000 loops, best of 3: 114 usec per loop
只有大约2倍的差异。但是,执行加法运算会导致相差约5倍:
mgilson$ python -m timeit "for k in range(5000): k+1"10000 loops, best of 3: 179 usec per loopmgilson$ python -m timeit -s "import numpy" "for k in numpy.arange(5000): k+1"1000 loops, best of 3: 786 usec per loop
为了好玩,让我们做一下添加:
$ python -m timeit -s "v = 1" "v + 1"10000000 loops, best of 3: 0.0261 usec per loopmgilson$ python -m timeit -s "import numpy; v = numpy.int64(1)" "v + 1"10000000 loops, best of 3: 0.121 usec per loop
最后,您的时间还包括不理想的列表/数组构建时间:
mgilson$ python -m timeit -s "v = range(5000)" "for k in v: k"10000 loops, best of 3: 80.2 usec per loopmgilson$ python -m timeit -s "import numpy; v = numpy.arange(5000)" "for k in v: k"1000 loops, best of 3: 237 usec per loop
注意,在这种情况下,numpy实际上离列表解决方案还很远。这表明 迭代 确实 要
慢一些,如果将numpy类型转换为标准python类型,则可能会加快速度。
1注意,切片时不会花费很多时间,因为那只需要分配O(1)新对象,因为numpy将 视图 返回到原始数组。



