本文将介绍列表推导的优势、底层原理和注意事项
- 列表推导的优势
-
- 列表推导快速的原因
- 列表推导的局限
列表推导的优势
列表推导的优势有两点一是语法简洁,例如:
l1 = [i for i in range(10)]
l2 = []
for i in range(10):
l2.append(i)
print(l1 == l2) # 输出:True
上面例子中l1采用列表推导生成,l2使用常规的Python代码生成。生成的列表l1、l2是相同的,但是明显看出l2更”啰嗦“。
第二个优点是快,列表推导要比常规的代码生成列表要快的多,例如:
start = time.time()
l1 = [i for i in range(10000000)]
end = time.time()
print("列表推导式用时:", end - start) # 列表推导式用时: 0.5695033073425293
l2 = []
start = time.time()
for i in range(10000000):
l2.append(i)
end = time.time()
print("正常写法用时:", end - start) # 正常写法用时: 1.5050091743469238
print(l1 == l2) # 输出:True
在博主2017款 mbp Cython 的运行环境下生成一千万元素的列表 列表推导的速度比for 循环的方式要快 2.5 倍左右。
列表推导快速的原因
我用下列代码作为实例:
def A():
l1 = [i for i in range(10000000)]
return l1
def B():
l2 = []
for i in range(10000000):
l2.append(i)
return l2
if __name__ == '__main__':
import dis
print("函数A的字节码:")
dis.dis(A)
print("函数B的字节码:")
dis.dis(B)
输出如下:
函数A的字节码:
7 0 LOAD_CONST 1
2 LOAD_CONST 2
4 MAKE_FUNCTION 0
6 LOAD_GLOBAL 0 (range)
8 LOAD_CONST 3 (10000000)
10 CALL_FUNCTION 1
12 GET_ITER
14 CALL_FUNCTION 1
16 STORE_FAST 0 (l1)
8 18 LOAD_FAST 0 (l1)
20 RETURN_VALUE
函数B的字节码:
11 0 BUILD_LIST 0
2 STORE_FAST 0 (l2)
12 4 SETUP_LOOP 26 (to 32)
6 LOAD_GLOBAL 0 (range)
8 LOAD_CONST 1 (10000000)
10 CALL_FUNCTION 1
12 GET_ITER
>> 14 FOR_ITER 14 (to 30)
16 STORE_FAST 1 (i)
13 18 LOAD_FAST 0 (l2)
20 LOAD_ATTR 1 (append)
22 LOAD_FAST 1 (i)
24 CALL_FUNCTION 1
26 POP_TOP
28 JUMP_ABSOLUTE 14
>> 30 POP_BLOCK
14 >> 32 LOAD_FAST 0 (l2)
34 RETURN_VALUE
从上图中的字节码可以看出,使用for循环的时候要先声明空列表,之后不断对 i 进行赋值,加载append方法,相比使用列表推导的方式多执行了很多字节码,所以
使用列表推导在解释器层面是做过优化的
,Python官方也推荐这个做法
列表推导的局限
- 不适合在for 循环中加入过于复杂的逻辑
- 在Python 2.x 中 列表推导中的变量会泄漏到列表推导之外,但在Python3中列表推导和其他的一些推导具有自己的变量作用域
- 可读性不高
转载注明出处