直接迭代序列要比通過索引迭代快?

前提:大齡編程初學者,入門Python。

問題:Python核心編程2上說直接迭代序列要比通過索引迭代快。不太理解,想了一下認為是因為直接迭代序列時因為序列存儲在內存中,只需要找到序列頭的指針然後順序讀取數據比通過索引查找數據省去了定址的時間么?

麻煩大神告知下,已Google過沒遇到想要的答案。


顯然不是,

如你所說的話,C++裡面vector的iter訪問就比下標訪問快了,顯然不是啦。

C++的比較可以看這個問題的答案C++ 的容器通過迭代器訪問和通過下標訪問有效率差別嗎? - STL

python的list內部實現類似C++的vector,在連續內存上構造,下標訪問都只需要做一次offset計算即可,下標訪問的開銷相對python這種腳本語言都是可以忽略的。

為什麼Python迭代就比下標訪問慢很多呢?

第一個原因就是python中一切皆對象,產生下標過程中涉及到了整數對象的構造,這期間有一段比較長的過程,還會涉及python 的gc。而迭代器是python內置的,底層是c語言實現的結構體。兩者的構造速度差別就非常大。

第二個原因可以考慮一下python的位元組碼

我們來考慮下面2段代碼

1.迭代器訪問

li = [1,2,3,4,5,6,7,8]
for i in li:
print i

2.下標訪問

li = [1,2,3,4,5,6,7,8]
for i in xrange(8):
print li[i]

1.產生的位元組碼

0 SETUP_LOOP 19 (to 22)

3 LOAD_GLOBAL 0 (li)

6 GET_ITER

&>&> 7 FOR_ITER 11 (to 21)

下面是print部分忽略

10 STORE_FAST 0 (i)

簡單解釋就是先載入全局變數li,然後獲取list li的內置迭代器,然後用解釋器的for_iter函數執行迭代。

2.產生的位元組碼

0SETUP_LOOP 29 (to 32)

3 LOAD_GLOBAL 0 (xrange)

6 LOAD_CONST 1 (8)

9 CALL_FUNCTION 1

12 GET_ITER

&>&> 13 FOR_ITER 15 (to 31)

16 STORE_FAST 0 (i)

19 LOAD_GLOBAL 1 (li)

22 LOAD_FAST 0 (i)

25 BINARY_SUBSCR

下面是print部分忽略

同樣也會用解釋器的for_iter函數執行迭代,但是多了4步驟,每次循環都要存儲i、載入li、載入i做下標運算。

下標運算的c語言代碼是:

case BINARY_SUBSCR:
w = POP();
v = TOP();
if (PyList_CheckExact(v) PyInt_CheckExact(w)) {
/* INLINE: list[int] */
Py_ssize_t i = PyInt_AsSsize_t(w);
if (i &< 0) i += PyList_GET_SIZE(v); if (i &>= 0 i &< PyList_GET_SIZE(v)) { x = PyList_GET_ITEM(v, i); Py_INCREF(x); } else goto slow_get; } else slow_get: x = PyObject_GetItem(v, w); Py_DECREF(v); Py_DECREF(w); SET_TOP(x); if (x != NULL) continue; break;

可以看到python對list的BINARY_SUBSCR做了優化,PyObject_GetItem是一個宏

#define PyList_GET_ITEM(op, i) (((PyListObject *)(op))-&>ob_item[i])

和一般數組訪問差不多。

總結就是,腳本語言底層的很多東西被掩蓋了,所以要盡量用語言自身的特性,不然會多出很多開銷。太晚了睡覺了。。


也許是range生成列表也需要一定時間…也許沒有被優化…再扯一點就是定址的時候做的加法耗了時間…(捂臉熊.jpg


推薦閱讀:

有哪些利用編程方法提高自己工作效率的例子?
[10] Python條件判斷語句(一)
【Kotlin填坑-02】使用高階函數後的when語句

TAG:Python | 編程 | 編程入門 | Python教程 |