在咱們平時寫代碼中,確定會遇到很多從一個列表向另外一個列表進行轉化的操做,以給列表中每一個int元素+1爲例,一般咱們會用到一下3種方式:python
array = range(1000) # 循環 a = [] for i in array: a.append(i+1) #map函數 a = map(lambda x: x+1, array) #列表推導 a = [x+1 for x in array]
究竟以上三種寫法有何差別,哪一種寫法最好,以前讀google的代碼規範說推薦第三種列表推導,那麼爲何推薦列表推導?app
咱們在ipython中用timeit進行一下簡單的評測:函數
#循環 array=range(1000) a=[] %timeit for i in array: a.append(i+1) # 1000 loops, best of 3: 156 us per loop #map函數 %timeit map(lambda x: x+1, array) # 10000 loops, best of 3: 172 us per loop #列表推導 %timeit [x+1 for x in array] #10000 loops, best of 3: 68.7 us per loop
能夠看出列表推導的優點是很是明顯的oop
爲何會形成這種狀況呢?咱們用dis模塊查看各個方法調用了哪些底層資源測試
def test_for(array): a = [] for i in array: a.append(i+1) return a dis.dis(test_for) 2 0 BUILD_LIST 0 3 STORE_FAST 1 (a) 3 6 SETUP_LOOP 31 (to 40) 9 LOAD_FAST 0 (array) 12 GET_ITER >> 13 FOR_ITER 23 (to 39) 16 STORE_FAST 2 (i) 4 19 LOAD_FAST 1 (a) 22 LOAD_ATTR 0 (append) 25 LOAD_FAST 2 (i) 28 LOAD_CONST 1 (1) 31 BINARY_ADD 32 CALL_FUNCTION 1 35 POP_TOP 36 JUMP_ABSOLUTE 13 >> 39 POP_BLOCK 5 >> 40 LOAD_FAST 1 (a) 43 RETURN_VALUE
能夠看出for循環中,在主循環體,程序反覆調用load和callgoogle
def test_map(array): return map(lambda x: x+1, array) dis.dis(test_map) 2 0 LOAD_GLOBAL 0 (map) 3 LOAD_CONST 1 (<code object <lambda> at 0x29e4cb0, file "<ipython-input-20-4aa500644b58>", line 2>) 6 MAKE_FUNCTION 0 9 LOAD_FAST 0 (array) 12 CALL_FUNCTION 2 15 RETURN_VALUE
map循環時構造了一個匿名函數,而且用map調用了該函數callspa
def test_list(array): return [x+1 for x in array] dis.dis(test_list): 2 0 BUILD_LIST 0 3 LOAD_FAST 0 (array) 6 GET_ITER >> 7 FOR_ITER 16 (to 26) 10 STORE_FAST 1 (x) 13 LOAD_FAST 1 (x) 16 LOAD_CONST 1 (1) 19 BINARY_ADD 20 LIST_APPEND 2 23 JUMP_ABSOLUTE 7 >> 26 RETURN_VALUE
列表推導竟然使用了LIST_APPEND這樣一個東西去記錄結果代碼規範
咱們都知道調用底層的速度會更快,因此說用列表推導的方式會更快一些,由於他並無調用其餘的函數code
那麼如何修改前兩種方法,使其速度更快呢?blog
1,for循環,咱們留意到for循環中有兩個步驟,一是load,而是call,若是把load的過程記錄下來,那麼速度就會更快一些
a = [] test_func = a.append %timeit for i in array: test_func(i+1) #10000 loops, best of 3: 100 us per loop
比較以前的寫法,有大幅度的提高
2,map函數,咱們在一開始的測試中使用的是咱們自定義的lambda匿名函數,若是將該匿名函數設置爲底層的簡單加法,那麼其速度也會有大幅提高
int_obj=1 %timeit map(int_obj.__add__, array) #10000 loops, best of 3: 67.6 us per loop
咱們驚奇的發現其速度和列表推導幾乎同樣
接下來就有一個問題:爲何有列表推導,還要有for呢?若是對於一個複雜的轉換操做,列表推導的效率其實和for是差很少的
def add(x): return x+1 %timeit [add(x) for x in array] #1000 loops, best of 3: 180 us per loop
總上所述:簡單的循環映射操做,咱們建議用列表推導形式,其效率更高,速度更快。複雜的循環映射操做,咱們建議用for循環,這樣的代碼更加易讀易懂。而對於map方法,咱們認爲這是一種過期的寫法,應當少用,甚至不用。
參考:https://www.zhihu.com/question/34637934
以上結果實測:python 2.7.3,ipython 0.12.1