眾所周知,Python 不是一種執(zhí)行效率較高的語(yǔ)言。此外在任何語(yǔ)言中,循環(huán)都是一種非常消耗時(shí)間的操作。假如任意一種簡(jiǎn)單的單步操作耗費(fèi)的時(shí)間為 1 個(gè)單位,將此操作重復(fù)執(zhí)行上萬(wàn)次,最終耗費(fèi)的時(shí)間也將增長(zhǎng)上萬(wàn)倍。
while
和 for
是 Python 中常用的兩種實(shí)現(xiàn)循環(huán)的關(guān)鍵字,它們的運(yùn)行效率實(shí)際上是有差距的。比如下面的測(cè)試代碼:
import timeit
def while_loop(n=100_000_000):
i = 0
s = 0
while i < n:
s += i
i += 1
return s
def for_loop(n=100_000_000):
s = 0
for i in range(n):
s += i
return s
def 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
這是一個(gè)簡(jiǎn)單的求和操作,計(jì)算從 1 到 n 之間所有自然數(shù)的總和。可以看到 for
循環(huán)相比 while
要快 1.5 秒。
其中的差距主要在于兩者的機(jī)制不同。
在每次循環(huán)中,while
實(shí)際上比 for
多執(zhí)行了兩步操作:邊界檢查和變量 i
的自增。即每進(jìn)行一次循環(huán),while 都會(huì)做一次邊界檢查 (while i < n
)和自增計(jì)算(i +=1
)。這兩步操作都是顯式的純 Python 代碼。
for
循環(huán)不需要執(zhí)行邊界檢查和自增操作,沒(méi)有增加顯式的 Python 代碼(純 Python 代碼效率低于底層的 C 代碼)。當(dāng)循環(huán)的次數(shù)足夠多,就出現(xiàn)了明顯的效率差距。
可以再增加兩個(gè)函數(shù),在 for
循環(huán)中加上不必要的邊界檢查和自增計(jì)算:
import timeit
def while_loop(n=100_000_000):
i = 0
s = 0
while i < n:
s += i
i += 1
return s
def for_loop(n=100_000_000):
s = 0
for i in range(n):
s += i
return s
def for_loop_with_inc(n=100_000_000):
s = 0
for i in range(n):
s += i
i += 1
return s
def for_loop_with_test(n=100_000_000):
s = 0
for i in range(n):
if i < n:
pass
s += i
return s
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('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
可以看出,增加的邊界檢查和自增操作確實(shí)大大影響了 for
循環(huán)的執(zhí)行效率。
前面提到過(guò),Python 底層的解釋器和內(nèi)置函數(shù)是用 C 語(yǔ)言實(shí)現(xiàn)的。而 C 語(yǔ)言的執(zhí)行效率遠(yuǎn)大于 Python。
對(duì)于上面的求等差數(shù)列之和的操作,借助于 Python 內(nèi)置的 sum
函數(shù),可以獲得遠(yuǎn)大于 for
或 while
循環(huán)的執(zhí)行效率。
import timeit
def while_loop(n=100_000_000):
i = 0
s = 0
while i < n:
s += i
i += 1
return s
def for_loop(n=100_000_000):
s = 0
for i in range(n):
s += i
return s
def 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
可以看到,使用內(nèi)置函數(shù) sum
替代循環(huán)之后,代碼的執(zhí)行效率實(shí)現(xiàn)了成倍的增長(zhǎng)。
內(nèi)置函數(shù) sum
的累加操作實(shí)際上也是一種循環(huán),但它由 C 語(yǔ)言實(shí)現(xiàn),而 for
循環(huán)中的求和操作是由純 Python 代碼 s += i
實(shí)現(xiàn)的。C > Python。
再拓展一下思維。小時(shí)候都聽(tīng)說(shuō)過(guò)童年高斯巧妙地計(jì)算 1 到 100 之和的故事。1…100 之和等于 (1 + 100) * 50。這個(gè)計(jì)算方法同樣可以應(yīng)用到上面的求和操作中。
import timeit
def while_loop(n=100_000_000):
i = 0
s = 0
while i < n:
s += i
i += 1
return s
def for_loop(n=100_000_000):
s = 0
for i in range(n):
s += i
return s
def sum_range(n=100_000_000):
return sum(range(n))
def math_sum(n=100_000_000):
return (n * (n - 1)) // 2
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))
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 的執(zhí)行時(shí)間約為 2.4e-6
,縮短了上百萬(wàn)倍。這里的思路就是,既然循環(huán)的效率低,一段代碼要重復(fù)執(zhí)行上億次。
索性直接不要循環(huán),通過(guò)數(shù)學(xué)公式,把上億次的循環(huán)操作變成只有一步操作。效率自然得到了空前的加強(qiáng)。
最后的結(jié)論(有點(diǎn)謎語(yǔ)人):
實(shí)現(xiàn)循環(huán)的最快方式—— —— ——就是不用循環(huán)
對(duì)于 Python 而言,則盡可能地使用內(nèi)置函數(shù),將循環(huán)中的純 Python 代碼降到最低。
審核編輯:湯梓紅
-
測(cè)試
+關(guān)注
關(guān)注
8文章
5399瀏覽量
127128 -
代碼
+關(guān)注
關(guān)注
30文章
4837瀏覽量
69128 -
python
+關(guān)注
關(guān)注
56文章
4811瀏覽量
85076
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
C語(yǔ)言關(guān)鍵字分別發(fā)生在哪個(gè)階段
Python中多線程和多進(jìn)程的區(qū)別
![<b class='flag-5'>Python</b><b class='flag-5'>中</b>多線程和多進(jìn)程的區(qū)別](https://file1.elecfans.com/web2/M00/0A/EF/wKgaomcYcmaAS08XAAAsH7JtzO0544.png)
Linux應(yīng)用層控制外設(shè)的兩種不同的方式
![Linux應(yīng)用層控制外設(shè)的<b class='flag-5'>兩種</b>不同的方式](https://file1.elecfans.com//web2/M00/09/78/wKgaomb4z66AfgSGAAEBbuY4Cyc846.jpg)
使用邊緣AI和Sitara處理器進(jìn)行關(guān)鍵字檢測(cè)
![使用邊緣AI和Sitara處理器進(jìn)行<b class='flag-5'>關(guān)鍵字</b>檢測(cè)](https://file.elecfans.com/web1/M00/D9/4E/pIYBAF_1ac2Ac0EEAABDkS1IP1s689.png)
typedef struct和直接struct的區(qū)別
wdm設(shè)備的兩種傳輸方式
快速掌握C語(yǔ)言關(guān)鍵字
![快速掌握C語(yǔ)言<b class='flag-5'>關(guān)鍵字</b>](https://file.elecfans.com/web2/M00/9B/3D/poYBAGQjnauAVXOgAABFcEbXdEE684.png)
控制器有哪兩種實(shí)現(xiàn)方式?各有何優(yōu)缺點(diǎn)?
伺服電機(jī)與步進(jìn)電機(jī)|兩種電機(jī)的關(guān)鍵區(qū)別
![伺服電機(jī)與步進(jìn)電機(jī)|<b class='flag-5'>兩種</b>電機(jī)的<b class='flag-5'>關(guān)鍵</b>區(qū)別](https://file1.elecfans.com/web2/M00/EF/2B/wKgZomZv70KAAYbpAUMAAMFYSBg622.png)
PCBA加工中常見(jiàn)的兩種焊接方式詳解
溫度沖擊與溫度循環(huán):揭示材料失效的兩種溫度試驗(yàn)方法
![溫度沖擊與溫度<b class='flag-5'>循環(huán)</b>:揭示材料失效的<b class='flag-5'>兩種</b>溫度試驗(yàn)方法](https://file1.elecfans.com/web2/M00/CD/E7/wKgaomYgxrSAKvkKAABWzLX_oCI810.png)
淺析多晶硅錠中位錯(cuò)存在的兩種來(lái)源
![淺析多晶硅錠<b class='flag-5'>中</b>位錯(cuò)存在的<b class='flag-5'>兩種</b>來(lái)源](https://file1.elecfans.com/web2/M00/C5/E7/wKgZomYDjliAau-NAAB4uCj343U531.png)
評(píng)論