3 回答

TA貢獻1911條經驗 獲得超7個贊
更多代碼行是否意味著性能降低的問題是有缺陷的,因為兩者都不是less lines ≠ performance ≠ more lines
。性能取決于 Python 代碼如何解析為執行它的底層 C 函數調用,這主要是解析器/解釋器的實現細節,與行數沒有一一對應。
也就是說,在選擇循環、列表理解和映射時需要考慮以下幾點。
當代碼的目的是創建某個內容的列表時,列表推導式通常比循環更快。據我所知,這與第二次調用追加方法有關,而且因為增量列表構造可能會導致列表大小調整,從而導致一些內存重新分配開銷。
無論在哪里調用,調用 python 函數都是一個緩慢的操作。引用這個偉大的答案:
A function call needs to manipulate the stack, pushing the local frame onto it, creating a new frame, then clear it all up again when the function returns.
一個例外是用 C 編寫的函數,例如來自操作員模塊的函數,它們比本地 Python 對應項稍快。
也就是說,我已經使用與此類似的代碼進行了一些分析,并且我的測試表明循環是最慢的選項。有趣的是,測試還表明地圖比列表理解稍快(與鏈接的答案相反)。代碼及結果如下:
ls = list(range(1000000))
def f(x):
? ? return x+1
def list_comprehension(ls):
? ? return [f(x) for x in ls]
def for_loop(ls):
? ? arr = []
? ? for x in ls:
? ? ? ? arr.append(f(x))
? ? return arr
def map_(ls):
? ? return list(map(f, ls))
if __name__ == "__main__":
? ? import cProfile
? ? for fn in [list_comprehension, for_loop, map_]:
? ? ? ? print('=' * 25)
? ? ? ? print("Profiling:", fn.__name__)
? ? ? ? print('=' * 25)
? ? ? ? pr = cProfile.Profile()
? ? ? ? for i in range(1000):
? ? ? ? ? ? pr.runcall(fn, ls)
? ? ? ? pr.create_stats()
? ? ? ? pr.print_stats()
# Output
=========================
Profiling: list_comprehension
=========================
? ? ? ? ?1000003000 function calls in 235.641 seconds
? ?Ordered by: standard name
? ?ncalls? tottime? percall? cumtime? percall filename:lineno(function)
1000000000? 104.000? ? 0.000? 104.000? ? 0.000 aa.py:5(f)
? ? ?1000? ? 0.008? ? 0.000? 235.640? ? 0.236 aa.py:8(list_comprehension)
? ? ?1000? 131.632? ? 0.132? 235.632? ? 0.236 aa.py:9(<listcomp>)
? ? ?1000? ? 0.001? ? 0.000? ? 0.001? ? 0.000 {method 'disable' of '_lsprof.Profiler' objects}
=========================
Profiling: for_loop
=========================
? ? ? ? ?2000002000 function calls in 410.884 seconds
? ?Ordered by: standard name
? ?ncalls? tottime? percall? cumtime? percall filename:lineno(function)
? ? ?1000? 242.083? ? 0.242? 410.883? ? 0.411 aa.py:11(for_loop)
1000000000? 107.727? ? 0.000? 107.727? ? 0.000 aa.py:5(f)
1000000000? ?61.073? ? 0.000? ?61.073? ? 0.000 {method 'append' of 'list' objects}
? ? ?1000? ? 0.001? ? 0.000? ? 0.001? ? 0.000 {method 'disable' of '_lsprof.Profiler' objects}
=========================
Profiling: map_
=========================
? ? ? ? ?1000002000 function calls in 205.035 seconds
? ?Ordered by: standard name
? ?ncalls? tottime? percall? cumtime? percall filename:lineno(function)
? ? ?1000? 102.451? ? 0.102? 205.034? ? 0.205 aa.py:17(map_)
1000000000? 102.583? ? 0.000? 102.583? ? 0.000 aa.py:5(f)
? ? ?1000? ? 0.001? ? 0.000? ? 0.001? ? 0.000 {method 'disable' of '_lsprof.Profiler' objects}
無論如何,在這種情況下最好的辦法是寫下所有不同的版本并自己進行分析,而不是依賴一般假設。

TA貢獻1993條經驗 獲得超6個贊
單行代碼可能比較長的代碼更快,但這不是一般規則。一些允許非常緊湊的代碼的 python 函數(如 any()、all() 等)經過優化,可以非常快速地執行,并且可能比 (例如)幾個執行相同工作的嵌套循環。編譯器/解釋器并不真正關心它必須處理多少文本(除非您的代碼由數千行代碼組成)。單行是首選,因為它節省了空間并使代碼更具可讀性。

TA貢獻1893條經驗 獲得超10個贊
行數與代碼的執行完全無關。py 文件將被讀取一次、解析和編譯。隨后,將執行編譯后的代碼。
換句話說,如果您創建一個包含 10 GB 空白的文件,是的,這會影響解析時間,但即使如此,執行也不會受到影響。
兩種變體之間的區別在于,一種使用列表理解來創建列表,而另一種則創建一個空列表,然后填充它。我的直覺是,列表理解理論上更容易優化,但如果你真的關心微小的差異,你應該分析它。
另一方面,執行時間的微小差異很少會導致代碼看起來更糟糕,因此默認情況下您應該始終選擇更優雅的解決方案。
添加回答
舉報