在咱们平时写代码中,确定会遇到很多从一个列表向另外一个列表进行转化的操做,以给列表中每一个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