天天看點

詳解Python清單推導(list comprehension)清單推導的優勢

本文将介紹清單推導的優勢、底層原理和注意事項

  • 清單推導的優勢
    • 清單推導快速的原因
    • 清單推導的局限

清單推導的優勢

清單推導的優勢有兩點一是文法簡潔,例如:

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官方也推薦這個做法

清單推導的局限

  1. 不适合在for 循環中加入過于複雜的邏輯
  2. 在Python 2.x 中 清單推導中的變量會洩漏到清單推導之外,但在Python3中清單推導和其他的一些推導具有自己的變量作用域
  3. 可讀性不高

轉載注明出處