新聞中心
Python程序運(yùn)行太慢的一個(gè)可能的原因是沒(méi)有盡可能的調(diào)用內(nèi)置方法,下面通過(guò)5個(gè)例子來(lái)演示如何用內(nèi)置方法提升Python程序的性能。

讓客戶(hù)滿(mǎn)意是我們工作的目標(biāo),不斷超越客戶(hù)的期望值來(lái)自于我們對(duì)這個(gè)行業(yè)的熱愛(ài)。我們立志把好的技術(shù)通過(guò)有效、簡(jiǎn)單的方式提供給客戶(hù),將通過(guò)不懈努力成為客戶(hù)在信息化領(lǐng)域值得信任、有價(jià)值的長(zhǎng)期合作伙伴,公司提供的服務(wù)項(xiàng)目有:域名注冊(cè)、網(wǎng)絡(luò)空間、營(yíng)銷(xiāo)軟件、網(wǎng)站建設(shè)、蟠龍網(wǎng)站維護(hù)、網(wǎng)站推廣。
1. 數(shù)組求平方和
輸入一個(gè)列表,要求計(jì)算出該列表中數(shù)字的的平方和。最終性能提升了1.4倍。首先創(chuàng)建一個(gè)長(zhǎng)度為10000的列表。
- arr = list(range(10000))
1.1 最常規(guī)的寫(xiě)法
while循環(huán)遍歷列表求平方和。平均運(yùn)行時(shí)間2.97毫秒。
- def sum_sqr_0(arr):
- res = 0
- n = len(arr)
- i = 0
- while i < n:
- res += arr[i] ** 2
- i += 1
- return res
- %timeit sum_sqr_0(arr)
- 2.97 ms ± 36.4 μs per loop (mean ± std. dev. of 7 runs, 100 loops each)
1.2 for range代替while循環(huán)
避免i += 1的變量類(lèi)型檢查帶來(lái)的額外開(kāi)銷(xiāo)。平均運(yùn)行時(shí)間2.9毫秒。
- def sum_sqr_1(arr):
- res = 0
- for i in range(len(arr)):
- res += arr[i] ** 2
- return res
- %timeit sum_sqr_1(arr)
- 2.9 ms ± 137 μs per loop (mean ± std. dev. of 7 runs, 100 loops each)
1.3 for x in arr代替for range
避免arr[i]的變量類(lèi)型檢查帶來(lái)的額外開(kāi)銷(xiāo)。平均運(yùn)行時(shí)間2.59毫秒。
- def sum_sqr_2(arr):
- res = 0
- for x in arr:
- res += x ** 2
- return res
- %timeit sum_sqr_2(arr)
- 2.59 ms ± 89 μs per loop (mean ± std. dev. of 7 runs, 100 loops each)
1.4 sum函數(shù)套用map函數(shù)
平均運(yùn)行時(shí)間2.36毫秒
- def sum_sqr_3(arr):
- return sum(map(lambda x: x**2, arr))
- %timeit sum_sqr_3(arr)
- 2.36 ms ± 15.1 μs per loop (mean ± std. dev. of 7 runs, 100 loops each)
1.5 sum函數(shù)套用生成器表達(dá)式
生成器表達(dá)式如果作為某個(gè)函數(shù)的參數(shù),則可以省略掉()。平均運(yùn)行時(shí)間2.35毫秒。
- def sum_sqr_4(arr):
- return sum(x ** 2 for x in arr)
- %timeit sum_sqr_4(arr)
- 2.35 ms ± 107 μs per loop (mean ± std. dev. of 7 runs, 100 loops each)
1. 6 sum函數(shù)套用列表推導(dǎo)式
平均運(yùn)行時(shí)間2.06毫秒。
- def sum_sqr_5(arr):
- return sum([x ** 2 for x in arr])
- %timeit sum_sqr_5(arr)
- 2.06 ms ± 27.2 μs per loop (mean ± std. dev. of 7 runs, 100 loops each)
2. 字符串拼接
輸入一個(gè)列表,要求將列表中的字符串的前3個(gè)字符都拼接為一個(gè)字符串。最終性能提升了2.1倍。
首先創(chuàng)建一個(gè)列表,生成10000個(gè)隨機(jī)長(zhǎng)度和內(nèi)容的字符串。
- from random import randint
- def random_letter():
- return chr(ord('a') + randint(0, 25))
- def random_letters(n):
- return "".join([random_letter() for _ in range(n)])
- strings = [random_letters(randint(1, 10)) for _ in range(10000)]
2.1 最常規(guī)的寫(xiě)法
while循環(huán)遍歷列表,對(duì)字符串進(jìn)行拼接。平均運(yùn)行時(shí)間1.86毫秒。
- def concat_strings_0(strings):
- res = ""
- n = len(strings)
- i = 0
- while i < n:
- res += strings[i][:3]
- i += 1
- return res
- %timeit concat_strings_0(strings)
- 1.86 ms ± 74.9 μs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
2.2 for range代替while循環(huán)
避免i += 1的變量類(lèi)型檢查帶來(lái)的額外開(kāi)銷(xiāo)。平均運(yùn)行時(shí)間1.55毫秒。
- def concat_strings_1(strings):
- res = ""
- for i in range(len(strings)):
- res += strings[i][:3]
- return res
- %timeit concat_strings_1(strings)
- 1.55 ms ± 32.9 μs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
2.3 for x in strings代替for range
避免strings[i]的變量類(lèi)型檢查帶來(lái)的額外開(kāi)銷(xiāo)。平均運(yùn)行時(shí)間1.32毫秒。
- def concat_strings_2(strings):
- res = ""
- for x in strings:
- res += x[:3]
- return res
- %timeit concat_strings_2(strings)
- 1.32 ms ± 19.5 μs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
2.4 .join方法套用生成器表達(dá)式
平均運(yùn)行時(shí)間1.06毫秒。
- def concat_strings_3(strings):
- return "".join(x[:3] for x in strings)
- %timeit concat_strings_3(strings)
- 1.06 ms ± 15.2 μs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
2.5 .join方法套用列表解析式
平均運(yùn)行時(shí)間0.85毫秒。
- def concat_strings_4(strings):
- return "".join([x[:3] for x in strings])
- %timeit concat_strings_4(strings)
- 858 μs ± 14.5 μs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
3. 篩選奇數(shù)
輸入一個(gè)列表,要求篩選出該列表中的所有奇數(shù)。最終性能提升了3.6倍。
首先創(chuàng)建一個(gè)長(zhǎng)度為10000的列表。
- arr = list(range(10000))
3.1 最常規(guī)的寫(xiě)法
創(chuàng)建一個(gè)空列表res,while循環(huán)遍歷列表,將奇數(shù)append到res中。平均運(yùn)行時(shí)間1.03毫秒。
- def filter_odd_0(arr):
- res = []
- i = 0
- n = len(arr)
- while i < n:
- if arr[i] % 2:
- res.append(arr[i])
- i += 1
- return res
- %timeit filter_odd_0(arr)
- 1.03 ms ± 34.1 μs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
3.2 for range代替while循環(huán)
避免i += 1的變量類(lèi)型檢查帶來(lái)的額外開(kāi)銷(xiāo)。平均運(yùn)行時(shí)間0.965毫秒。
- def filter_odd_1(arr):
- res = []
- for i in range(len(arr)):
- if arr[i] % 2:
- res.append(arr[i])
- i += 1
- return res
- %timeit filter_odd_1(arr)
- 965 μs ± 4.02 μs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
3.3 for x in arr代替for range
避免arr[i]的變量類(lèi)型檢查帶來(lái)的額外開(kāi)銷(xiāo)。平均運(yùn)行時(shí)間0.430毫秒。
- def filter_odd_2(arr):
- res = []
- for x in arr:
- if x % 2:
- res.append(x)
- return res
- %timeit filter_odd_2(arr)
- 430 μs ± 9.25 μs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
3.4 list套用filter函數(shù)
平均運(yùn)行時(shí)間0.763毫秒。注意filter函數(shù)很慢,在Python 3.6里非常雞肋。
- def filter_odd_3(arr):
- return list(filter(lambda x: x % 2, arr))
- %timeit filter_odd_3(arr)
- 763 μs ± 15.9 μs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
3.5 list套用生成器表達(dá)式
平均運(yùn)行時(shí)間0.398毫秒。
- def filter_odd_4(arr):
- return list((x for x in arr if x % 2))
- %timeit filter_odd_4(arr)
- 398 μs ± 16.4 μs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
3.6 帶條件的列表推導(dǎo)式
平均運(yùn)行時(shí)間0.290毫秒。
- def filter_odd_5(arr):
- return [x for x in arr if x % 2]
- %timeit filter_odd_5(arr)
- 290 μs ± 5.54 μs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
4. 兩個(gè)數(shù)組相加
輸入兩個(gè)長(zhǎng)度相同的列表,要求計(jì)算出兩個(gè)列表對(duì)應(yīng)位置的數(shù)字之和,返回一個(gè)與輸入長(zhǎng)度相同的列表。最終性能提升了2.7倍。
首先生成兩個(gè)長(zhǎng)度為10000的列表。
- arr1 = list(range(10000))
- arr2 = list(range(10000))
4.1 最常規(guī)的寫(xiě)法
創(chuàng)建一個(gè)空列表res,while循環(huán)遍歷列表,將兩個(gè)列表對(duì)應(yīng)的元素之和append到res中。平均運(yùn)行時(shí)間1.23毫秒。
- def arr_sum_0(arr1, arr2):
- i = 0
- n = len(arr1)
- res = []
- while i < n:
- res.append(arr1[i] + arr2[i])
- i += 1
- return res
- %timeit arr_sum_0(arr1, arr2)
- 1.23 ms ± 3.77 μs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
4.2 for range代替while循環(huán)
避免i += 1的變量類(lèi)型檢查帶來(lái)的額外開(kāi)銷(xiāo)。平均運(yùn)行時(shí)間0.997毫秒。
- def arr_sum_1(arr1, arr2):
- res = []
- for i in range(len(arr1)):
- res.append(arr1[i] + arr2[i])
- return res
- %timeit arr_sum_1(arr1, arr2)
- 997 μs ± 7.42 μs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
4.3 for i, x in enumerate代替for range
部分避免arr[i]的變量類(lèi)型檢查帶來(lái)的額外開(kāi)銷(xiāo)。平均運(yùn)行時(shí)間0.799毫秒。
- def arr_sum_2(arr1, arr2):
- res = arr1.copy()
- for i, x in enumerate(arr2):
- res[i] += x
- return res
- %timeit arr_sum_2(arr1, arr2)
- 799 μs ± 16.7 μs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
4.4 for x, y in zip代替for range
避免arr[i]的變量類(lèi)型檢查帶來(lái)的額外開(kāi)銷(xiāo)。平均運(yùn)行時(shí)間0.769毫秒。
- def arr_sum_3(arr1, arr2):
- res = []
- for x, y in zip(arr1, arr2):
- res.append(x + y)
- return res
- %timeit arr_sum_3(arr1, arr2)
- 769 μs ± 12.2 μs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
4.5 列表推導(dǎo)式套用zip
平均運(yùn)行時(shí)間0.462毫秒。
- def arr_sum_4(arr1, arr2):
- return [x + y for x, y in zip(arr1, arr2)]
- %timeit arr_sum_4(arr1, arr2)
- 462 μs ± 3.43 μs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
5. 兩個(gè)列表相同元素的數(shù)量
輸入兩個(gè)列表,要求統(tǒng)計(jì)兩個(gè)列表相同元素的數(shù)量。其中每個(gè)列表內(nèi)的元素都是不重復(fù)的。最終性能提升了5000倍。
首先創(chuàng)建兩個(gè)列表,并將元素的順序打亂。
- from random import shuffle
- arr1 = list(range(2000))
- shuffle(arr1)
- arr2 = list(range(1000, 3000))
- shuffle(arr2)
5.1 最常規(guī)的寫(xiě)法
while循環(huán)嵌套,判斷元素arr1[i]是否等于arr2[j],平均運(yùn)行時(shí)間338毫秒。
- def n_common_0(arr1, arr2):
- res = 0
- i = 0
- m = len(arr1)
- n = len(arr2)
- while i < m:
- j = 0
- while j < n:
- if arr1[i] == arr2[j]:
- res += 1
- j += 1
- i += 1
- return res
- %timeit n_common_0(arr1, arr2)
- 338 ms ± 7.81 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
5.2 for range代替while循環(huán)
避免i += 1的變量類(lèi)型檢查帶來(lái)的額外開(kāi)銷(xiāo)。平均運(yùn)行時(shí)間233毫秒。
- def n_common_1(arr1, arr2):
- res = 0
- for i in range(len(arr1)):
- for j in range(len(arr2)):
- if arr1[i] == arr2[j]:
- res += 1
- return res
- %timeit n_common_1(arr1, arr2)
- 233 ms ± 10.9 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
5.3 for x in arr代替for range
避免arr[i]的變量類(lèi)型檢查帶來(lái)的額外開(kāi)銷(xiāo)。平均運(yùn)行時(shí)間84.8毫秒。
- def n_common_2(arr1, arr2):
- res = 0
- for x in arr1:
- for y in arr2:
- if x == y:
- res += 1
- return res
- %timeit n_common_2(arr1, arr2)
- 84.8 ms ± 1.38 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
5.4 使用if x in arr2代替內(nèi)層循環(huán)
平均運(yùn)行時(shí)間24.9毫秒。
- def n_common_3(arr1, arr2):
- res = 0
- for x in arr1:
- if x in arr2:
- res += 1
- return res
- %timeit n_common_3(arr1, arr2)
- 24.9 ms ± 1.39 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
5.4 使用更快的算法
將數(shù)組用.sort方法排序,再進(jìn)行單層循環(huán)遍歷。把時(shí)間復(fù)雜度從O(n2)降低到O(nlogn),平均運(yùn)行時(shí)間0.239毫秒。
- def n_common_4(arr1, arr2):
- arr1.sort()
- arr2.sort()
- res = i = j = 0
- m, n = len(arr1), len(arr2)
- while i < m and j < n:
- if arr1[i] == arr2[j]:
- res += 1
- i += 1
- j += 1
- elif arr1[i] > arr2[j]:
- j += 1
- else:
- i += 1
- return res
- %timeit n_common_4(arr1, arr2)
- 329 μs ± 12.3 μs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
5.5 使用更好的數(shù)據(jù)結(jié)構(gòu)
將數(shù)組轉(zhuǎn)為集合,求交集的長(zhǎng)度。平均運(yùn)行時(shí)間0.067毫秒。
- def n_common_5(arr1, arr2):
- return len(set(arr1) & set(arr2))
- %timeit n_common_5(arr1, arr2)
- 67.2 μs ± 755 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
【責(zé)任編輯:龐桂玉 TEL:(010)68476606】
文章名稱(chēng):程序運(yùn)行慢?你怕是寫(xiě)的假Python
URL地址:http://fisionsoft.com.cn/article/cdccdpo.html


咨詢(xún)
建站咨詢(xún)
