本文将介紹清單推導的優勢、底層原理和注意事項
- 清單推導的優勢
-
- 清單推導快速的原因
- 清單推導的局限
清單推導的優勢
清單推導的優勢有兩點一是文法簡潔,例如:
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中清單推導和其他的一些推導具有自己的變量作用域
- 可讀性不高
轉載注明出處