Python 实现循环的最快方式(for、while 等速度对比)
作者:StarryLand
來源:https://www.starky.ltd/2021/11/23/the-fastest-way-to-loop-in-python
眾所周知,Python 不是一種執行效率較高的語言。此外在任何語言中,循環都是一種非常消耗時間的操作。假如任意一種簡單的單步操作耗費的時間為 1 個單位,將此操作重復執行上萬次,最終耗費的時間也將增長上萬倍。
while 和 for 是 Python 中常用的兩種實現循環的關鍵字,它們的運行效率實際上是有差距的。比如下面的測試代碼:
import?timeitdef?while_loop(n=100_000_000):i?=?0s?=?0while?i?<?n:s?+=?ii?+=?1return?sdef?for_loop(n=100_000_000):s?=?0for?i?in?range(n):s?+=?ireturn?sdef?main():print('while?loop\t\t',?timeit.timeit(while_loop,?number=1))print('for?loop\t\t',?timeit.timeit(for_loop,?number=1))if?__name__?==?'__main__':main() #?=>?while?loop???????????????4.718853999860585 #?=>?for?loop?????????????????3.211570399813354這是一個簡單的求和操作,計算從 1 到 n 之間所有自然數的總和。可以看到 for 循環相比 while 要快 1.5 秒。
其中的差距主要在于兩者的機制不同。
在每次循環中,while 實際上比 for 多執行了兩步操作:邊界檢查和變量 i 的自增。即每進行一次循環,while 都會做一次邊界檢查 (while i < n)和自增計算(i +=1)。這兩步操作都是顯式的純 Python 代碼。
for 循環不需要執行邊界檢查和自增操作,沒有增加顯式的 Python 代碼(純 Python 代碼效率低于底層的 C 代碼)。當循環的次數足夠多,就出現了明顯的效率差距。
可以再增加兩個函數,在 for 循環中加上不必要的邊界檢查和自增計算:
import?timeitdef?while_loop(n=100_000_000):i?=?0s?=?0while?i?<?n:s?+=?ii?+=?1return?sdef?for_loop(n=100_000_000):s?=?0for?i?in?range(n):s?+=?ireturn?sdef?for_loop_with_inc(n=100_000_000):s?=?0for?i?in?range(n):s?+=?ii?+=?1return?sdef?for_loop_with_test(n=100_000_000):s?=?0for?i?in?range(n):if?i?<?n:passs?+=?ireturn?sdef?main():print('while?loop\t\t',?timeit.timeit(while_loop,?number=1))print('for?loop\t\t',?timeit.timeit(for_loop,?number=1))print('for?loop?with?increment\t\t',timeit.timeit(for_loop_with_inc,?number=1))print('for?loop?with?test\t\t',?timeit.timeit(for_loop_with_test,?number=1))if?__name__?==?'__main__':main() #?=>?while?loop???????????????4.718853999860585 #?=>?for?loop?????????????????3.211570399813354 #?=>?for?loop?with?increment??????????4.602369500091299 #?=>?for?loop?with?test???????????????4.18337869993411可以看出,增加的邊界檢查和自增操作確實大大影響了 for 循環的執行效率。
前面提到過,Python 底層的解釋器和內置函數是用 C 語言實現的。而 C 語言的執行效率遠大于 Python。
對于上面的求等差數列之和的操作,借助于 Python 內置的 sum 函數,可以獲得遠大于 for 或 while 循環的執行效率。
import?timeitdef?while_loop(n=100_000_000):i?=?0s?=?0while?i?<?n:s?+=?ii?+=?1return?sdef?for_loop(n=100_000_000):s?=?0for?i?in?range(n):s?+=?ireturn?sdef?sum_range(n=100_000_000):return?sum(range(n))def?main():print('while?loop\t\t',?timeit.timeit(while_loop,?number=1))print('for?loop\t\t',?timeit.timeit(for_loop,?number=1))print('sum?range\t\t',?timeit.timeit(sum_range,?number=1))if?__name__?==?'__main__':main() #?=>?while?loop???????????????4.718853999860585 #?=>?for?loop?????????????????3.211570399813354 #?=>?sum?range????????????????0.8658821999561042可以看到,使用內置函數 sum 替代循環之后,代碼的執行效率實現了成倍的增長。
內置函數 sum 的累加操作實際上也是一種循環,但它由 C 語言實現,而 for 循環中的求和操作是由純 Python 代碼 s += i 實現的。C > Python。
再拓展一下思維。小時候都聽說過童年高斯巧妙地計算 1 到 100 之和的故事。1…100 之和等于 (1 + 100) * 50。這個計算方法同樣可以應用到上面的求和操作中。
import?timeitdef?while_loop(n=100_000_000):i?=?0s?=?0while?i?<?n:s?+=?ii?+=?1return?sdef?for_loop(n=100_000_000):s?=?0for?i?in?range(n):s?+=?ireturn?sdef?sum_range(n=100_000_000):return?sum(range(n))def?math_sum(n=100_000_000):return?(n?*?(n?-?1))?//?2def?main():print('while?loop\t\t',?timeit.timeit(while_loop,?number=1))print('for?loop\t\t',?timeit.timeit(for_loop,?number=1))print('sum?range\t\t',?timeit.timeit(sum_range,?number=1))print('math?sum\t\t',?timeit.timeit(math_sum,?number=1))if?__name__?==?'__main__':main() #?=>?while?loop???????????????4.718853999860585 #?=>?for?loop?????????????????3.211570399813354 #?=>?sum?range????????????????0.8658821999561042 #?=>?math?sum?????????????????2.400018274784088e-06最終 math sum 的執行時間約為 2.4e-6,縮短了上百萬倍。這里的思路就是,既然循環的效率低,一段代碼要重復執行上億次。
索性直接不要循環,通過數學公式,把上億次的循環操作變成只有一步操作。效率自然得到了空前的加強。
最后的結論(有點謎語人):
實現循環的最快方式—— —— ——就是不用循環
對于 Python 而言,則盡可能地使用內置函數,將循環中的純 Python 代碼降到最低。
參考資料
The Fastest Way to Loop in Python - mCoding? (https://youtu.be/Qgevy75co8c)
各位伙伴們好,詹帥本帥搭建了一個個人博客和小程序,匯集各種干貨和資源,也方便大家閱讀,感興趣的小伙伴請移步小程序體驗一下哦!(歡迎提建議)
推薦閱讀
牛逼!Python常用數據類型的基本操作(長文系列第①篇)
牛逼!Python的判斷、循環和各種表達式(長文系列第②篇)
牛逼!Python函數和文件操作(長文系列第③篇)
牛逼!Python錯誤、異常和模塊(長文系列第④篇)
總結
以上是生活随笔為你收集整理的Python 实现循环的最快方式(for、while 等速度对比)的全部內容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: 再见Xshell!这个开源的终端工具更酷
- 下一篇: 2000字详解 当Pandas遇上超大规
