簡體   English   中英

為什么itertools.chain比扁平化列表理解更快?

[英]Why is itertools.chain faster than a flattening list comprehension?

這個問題的評論中討論的上下文中提到,雖然連接一個字符串序列只需要''.join([str1, str2, ...]) ,但連接一系列列表就像list(itertools.chain(lst1, lst2, ...))一樣list(itertools.chain(lst1, lst2, ...)) ,雖然你也可以使用[x for y in [lst1, lst2, ...] for x in y]的列表理解。 讓我感到驚訝的是,第一種方法始終比第二種方法更快:

import random
import itertools

random.seed(100)
lsts = [[1] * random.randint(100, 1000) for i in range(1000)]

%timeit [x for y in lsts for x in y]
# 39.3 ms ± 436 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
%timeit list(itertools.chain.from_iterable(lsts))
# 30.6 ms ± 866 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
%timeit list(x for y in lsts for x in y)  # Proposed in comments
# 62.5 ms ± 504 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
# Loop-based methods proposed in the comments
%%timeit
a = []
for lst in lsts: a += lst
# 26.4 ms ± 634 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
%%timeit
a = []
for lst in lsts: a.extend(lst)
# 26.7 ms ± 728 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

它不是數量級的差異,但也不可忽略。 我想知道可能是這種情況,因為列表推導通常是解決給定問題的最快方法之一。 起初我認為也許itertools.chain對象可能有一個len list構造函數可以用來預分配必要的內存,但事實並非如此(不能在itertools.chain對象上調用len )。 一些自定義的itertools.chain -to- list轉換是以某種方式發生的還是itertools.chain利用其他一些機制?

在Windows 10 x64上測試Python 3.6.3,如果相關的話。

編輯:

這似乎是最快的方法,畢竟調用。 .extend每個列表的空列表,如@zwer所提議的,可能是因為它適用於數據的“塊”而不是基於每個元素。

這是itertools.chain.from_iterable 即使您不了解C也不難閱讀,並且您可以告訴所有事情都發生在c級別(在用於在代碼中生成列表之前)。

列表推導的字節碼如下:

def f(lsts):
    return [x for y in lsts for x in y]

dis.dis(f.__code__.co_consts[1])
  2           0 BUILD_LIST               0
              2 LOAD_FAST                0 (.0)
        >>    4 FOR_ITER                18 (to 24)
              6 STORE_FAST               1 (y)
              8 LOAD_FAST                1 (y)
             10 GET_ITER
        >>   12 FOR_ITER                 8 (to 22)
             14 STORE_FAST               2 (x)
             16 LOAD_FAST                2 (x)
             18 LIST_APPEND              3
             20 JUMP_ABSOLUTE           12
        >>   22 JUMP_ABSOLUTE            4
        >>   24 RETURN_VALUE

這些是創建列表理解所涉及的所有python解釋器操作。 只需要在C級( chain )執行所有操作,而不是讓解釋器跨過每個字節代碼步驟(在理解中),這將為您提供性能提升。

不過,這種提升是如此之小,我不擔心。 這是python,可讀性超速。


編輯:

對於列表包裹的生成器理解

def g(lists):
    return list(x for y in lsts for x in y)

# the comprehension
dis.dis(g.__code__.co_consts[1])
  2           0 LOAD_FAST                0 (.0)
        >>    2 FOR_ITER                20 (to 24)
              4 STORE_FAST               1 (y)
              6 LOAD_FAST                1 (y)
              8 GET_ITER
        >>   10 FOR_ITER                10 (to 22)
             12 STORE_FAST               2 (x)
             14 LOAD_FAST                2 (x)
             16 YIELD_VALUE
             18 POP_TOP
             20 JUMP_ABSOLUTE           10
        >>   22 JUMP_ABSOLUTE            2
        >>   24 LOAD_CONST               0 (None)
             26 RETURN_VALUE

因此,解釋器在運行由列表解壓縮的生成器表達式時有相似的步驟數,但正如您所料,使list解包生成器(而不是C LIST_APPEND指令)的python級開銷是什么減慢它失敗了。

dis.dis(f)
  2           0 LOAD_CONST               1 (<code object <listcomp> at 0x000000000FB58B70, file "<ipython-input-33-1d46ced34d66>", line 2>)
              2 LOAD_CONST               2 ('f.<locals>.<listcomp>')
              4 MAKE_FUNCTION            0
              6 LOAD_FAST                0 (lsts)
              8 GET_ITER
             10 CALL_FUNCTION            1
             12 RETURN_VALUE

dis.dis(g)
  2           0 LOAD_GLOBAL              0 (list)
              2 LOAD_CONST               1 (<code object <genexpr> at 0x000000000FF6F420, file "<ipython-input-40-0334a7cdeb8f>", line 2>)
              4 LOAD_CONST               2 ('g.<locals>.<genexpr>')
              6 MAKE_FUNCTION            0
              8 LOAD_GLOBAL              1 (lsts)
             10 GET_ITER
             12 CALL_FUNCTION            1
             14 CALL_FUNCTION            1
             16 RETURN_VALUE

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM