眾所周知奖亚,Python 不是一種執(zhí)行效率較高的語言淳梦。此外在任何語言中,循環(huán)都是一種非常消耗時間的操作遂蛀。假如任意一種簡單的單步操作耗費的時間為 1 個單位谭跨,將此操作重復執(zhí)行上萬次干厚,最終耗費的時間也將增長上萬倍李滴。
while 和 for 是 Python 中常用的兩種實現(xiàn)循環(huán)的關鍵字,它們的運行效率實際上是有差距的蛮瞄。比如下面的測試代碼:
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
這是一個簡單的求和操作所坯,計算從 1 到 n 之間所有自然數的總和」彝保可以看到 for 循環(huán)相比 while 要快 1.5 秒芹助。
其中的差距主要在于兩者的機制不同。
在每次循環(huán)中闲先,while 實際上比 for 多執(zhí)行了兩步操作:邊界檢查和變量 i 的自增状土。即每進行一次循環(huán),while 都會做一次邊界檢查 (while i < n)和自增計算(i +=1)伺糠。這兩步操作都是顯式的純 Python 代碼蒙谓。
for 循環(huán)不需要執(zhí)行邊界檢查和自增操作,沒有增加顯式的 Python 代碼(純 Python 代碼效率低于底層的 C 代碼)训桶。當循環(huán)的次數足夠多累驮,就出現(xiàn)了明顯的效率差距酣倾。
可以再增加兩個函數,在 for 循環(huán)中加上不必要的邊界檢查和自增計算:
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
可以看出谤专,增加的邊界檢查和自增操作確實大大影響了 for 循環(huán)的執(zhí)行效率躁锡。
前面提到過,Python 底層的解釋器和內置函數是用 C 語言實現(xiàn)的置侍。而 C 語言的執(zhí)行效率遠大于 Python映之。
對于上面的求等差數列之和的操作,借助于 Python 內置的 sum 函數蜡坊,可以獲得遠大于 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
可以看到,使用內置函數 sum 替代循環(huán)之后算色,代碼的執(zhí)行效率實現(xiàn)了成倍的增長抬伺。
內置函數 sum 的累加操作實際上也是一種循環(huán),但它由 C 語言實現(xiàn)灾梦,而 for 循環(huán)中的求和操作是由純 Python 代碼 s += i 實現(xiàn)的峡钓。C > Python。
再拓展一下思維若河。小時候都聽說過童年高斯巧妙地計算 1 到 100 之和的故事能岩。1…100 之和等于 (1 + 100) * 50。這個計算方法同樣可以應用到上面的求和操作中萧福。
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í)行時間約為 2.4e-6拉鹃,縮短了上百萬倍。這里的思路就是鲫忍,既然循環(huán)的效率低膏燕,一段代碼要重復執(zhí)行上億次。
索性直接不要循環(huán)悟民,通過數學公式坝辫,把上億次的循環(huán)操作變成只有一步操作。效率自然得到了空前的加強射亏。
最后的結論(有點謎語人):
實現(xiàn)循環(huán)的最快方式—— —— ——就是不用循環(huán)
對于 Python 而言近忙,則盡可能地使用內置函數,將循環(huán)中的純 Python 代碼降到最低智润。
小編不是在講廢話及舍,只是給剛學Python的小伙伴少走彎路的方法!?弑痢锯玛!