為啥別人執行程式那麼快,而你的卻是龜速?
Python程式執行太慢的一個可能的原因是沒有儘可能的呼叫內建方法,下面通過5個例子來演示如何用內建方法提升PythGon程式的效能。
1. 陣列求平方和
輸入一個列表,要求計算出該列表中數字的的平方和。最終效能提升了1.4倍。
首先建立一個長度為10000的列表。
arr=list(range(10000))
1.1 最常規的寫法
while迴圈遍歷列表求平方和。平均執行時間2.97毫秒。
defsum_sqr_0(arr): res=0 n=len(arr) i=0 whilei<n: res+=arr[i]**2 i+=1 returnres %timeitsum_sqr_0(arr) 2.97ms±36.4µsperloop(mean±std.dev.of7runs,100loopseach)
很多人學習python,不知道從何學起。
很多人學習python,掌握了基本語法過後,不知道在哪裡尋找案例上手。
很多已經做案例的人,卻不知道如何去學習更加高深的知識。
那麼針對這三類人,我給大家提供一個好的學習平臺,免費領取視訊教程,電子書籍,以及課程的原始碼!
QQ群:609616831
1.2 for range代替while迴圈
避免i += 1
的變數型別檢查帶來的額外開銷。平均執行時間2.9毫秒。
defsum_sqr_1(arr): res=0 foriinrange(len(arr)): res+=arr[i]**2 returnres %timeitsum_sqr_1(arr) 2.9ms±137µsperloop(mean±std.dev.of7runs,100loopseach)
1.3for x in arr
代替for range
避免arr[i]
的變數型別檢查帶來的額外開銷。平均執行時間2.59毫秒。
defsum_sqr_2(arr):
res=0
forxinarr:
res+=x**2
returnres
%timeitsum_sqr_2(arr)
2.59ms±89µsperloop(mean±std.dev.of7runs,100loopseach)
1.4 sum函式套用map函式
平均執行時間2.36毫秒
defsum_sqr_3(arr): returnsum(map(lambdax:x**2,arr)) %timeitsum_sqr_3(arr) 2.36ms±15.1µsperloop(mean±std.dev.of7runs,100loopseach)
1.5 sum函式套用生成器表示式
生成器表示式如果作為某個函式的引數,則可以省略掉()。平均執行時間2.35毫秒。
defsum_sqr_4(arr):
returnsum(x**2forxinarr)
%timeitsum_sqr_4(arr)
2.35ms±107µsperloop(mean±std.dev.of7runs,100loopseach)
1. 6 sum函式套用列表推導式
平均執行時間2.06毫秒。
defsum_sqr_5(arr):
returnsum([x**2forxinarr])
%timeitsum_sqr_5(arr)
2.06ms±27.2µsperloop(mean±std.dev.of7runs,100loopseach)
2. 字串拼接
輸入一個列表,要求將列表中的字串的前3個字元都拼接為一個字串。最終效能提升了2.1倍。
首先建立一個列表,生成10000個隨機長度和內容的字串。
fromrandomimportrandint
defrandom_letter():
returnchr(ord('a')+randint(0,25))
defrandom_letters(n):
return"".join([random_letter()for_inrange(n)])
strings=[random_letters(randint(1,10))for_inrange(10000)]
2.1 最常規的寫法
while迴圈遍歷列表,對字串進行拼接。平均執行時間1.86毫秒。
defconcat_strings_0(strings):
res=""
n=len(strings)
i=0
whilei<n:
res+=strings[i][:3]
i+=1
returnres
%timeitconcat_strings_0(strings)
1.86ms±74.9µsperloop(mean±std.dev.of7runs,1000loopseach)
2.2for range
代替while迴圈
避免i += 1
的變數型別檢查帶來的額外開銷。平均執行時間1.55毫秒。
defconcat_strings_1(strings):
res=""
foriinrange(len(strings)):
res+=strings[i][:3]
returnres
%timeitconcat_strings_1(strings)
1.55ms±32.9µsperloop(mean±std.dev.of7runs,1000loopseach)
2.3for x in strings
代替for range
避免strings[i]的變數型別檢查帶來的額外開銷。平均執行時間1.32毫秒。
defconcat_strings_2(strings):
res=""
forxinstrings:
res+=x[:3]
returnres
%timeitconcat_strings_2(strings)
1.32ms±19.5µsperloop(mean±std.dev.of7runs,1000loopseach)
2.4 .join
方法套用生成器表示式
平均執行時間1.06毫秒。
defconcat_strings_3(strings):
return"".join(x[:3]forxinstrings)
%timeitconcat_strings_3(strings)
1.06ms±15.2µsperloop(mean±std.dev.of7runs,1000loopseach)
2.5 .join
方法套用列表解析式
平均執行時間0.85毫秒。
defconcat_strings_4(strings):
return"".join([x[:3]forxinstrings])
%timeitconcat_strings_4(strings)
858µs±14.5µsperloop(mean±std.dev.of7runs,1000loopseach)
3. 篩選奇數
輸入一個列表,要求篩選出該列表中的所有奇數。最終效能提升了3.6倍。
首先建立一個長度為10000的列表。
arr=list(range(10000))
3.1 最常規的寫法
建立一個空列表res,while迴圈遍歷列表,將奇數append到res中。平均執行時間1.03毫秒。
deffilter_odd_0(arr):
res=[]
i=0
n=len(arr)
whilei<n:
ifarr[i]%2:
res.append(arr[i])
i+=1
returnres
%timeitfilter_odd_0(arr)
1.03ms±34.1µsperloop(mean±std.dev.of7runs,1000loopseach)
3.2for range
代替while迴圈
避免i += 1
的變數型別檢查帶來的額外開銷。平均執行時間0.965毫秒。
deffilter_odd_1(arr):
res=[]
foriinrange(len(arr)):
ifarr[i]%2:
res.append(arr[i])
i+=1
returnres
%timeitfilter_odd_1(arr)
965µs±4.02µsperloop(mean±std.dev.of7runs,1000loopseach)
3.3for x in arr
代替for range
避免arr[i]
的變數型別檢查帶來的額外開銷。平均執行時間0.430毫秒。
deffilter_odd_2(arr):
res=[]
forxinarr:
ifx%2:
res.append(x)
returnres
%timeitfilter_odd_2(arr)
430µs±9.25µsperloop(mean±std.dev.of7runs,1000loopseach)
3.4 list套用filter函式
平均執行時間0.763毫秒。注意filter函式很慢,在Python 3.6裡非常雞肋。
deffilter_odd_3(arr):
returnlist(filter(lambdax:x%2,arr))
%timeitfilter_odd_3(arr)
763µs±15.9µsperloop(mean±std.dev.of7runs,1000loopseach)
3.5 list套用生成器表示式
平均執行時間0.398毫秒。
deffilter_odd_4(arr):
returnlist((xforxinarrifx%2))
%timeitfilter_odd_4(arr)
398µs±16.4µsperloop(mean±std.dev.of7runs,1000loopseach)
3.6 帶條件的列表推導式
平均執行時間0.290毫秒。
deffilter_odd_5(arr):
return[xforxinarrifx%2]
%timeitfilter_odd_5(arr)
290µs±5.54µsperloop(mean±std.dev.of7runs,1000loopseach)
4. 兩個陣列相加
輸入兩個長度相同的列表,要求計算出兩個列表對應位置的數字之和,返回一個與輸入長度相同的列表。最終效能提升了2.7倍。
首先生成兩個長度為10000的列表。
arr1=list(range(10000))
arr2=list(range(10000))
4.1 最常規的寫法
建立一個空列表res,while迴圈遍歷列表,將兩個列表對應的元素之和append到res中。平均執行時間1.23毫秒。
defarr_sum_0(arr1,arr2):
i=0
n=len(arr1)
res=[]
whilei<n:
res.append(arr1[i]+arr2[i])
i+=1
returnres
%timeitarr_sum_0(arr1,arr2)
1.23ms±3.77µsperloop(mean±std.dev.of7runs,1000loopseach)
4.2for range
代替while迴圈
避免i += 1
的變數型別檢查帶來的額外開銷。平均執行時間0.997毫秒。
defarr_sum_1(arr1,arr2):
res=[]
foriinrange(len(arr1)):
res.append(arr1[i]+arr2[i])
returnres
%timeitarr_sum_1(arr1,arr2)
997µs±7.42µsperloop(mean±std.dev.of7runs,1000loopseach)
4.3for i, x in enumerate
代替for range
部分避免arr[i]
的變數型別檢查帶來的額外開銷。平均執行時間0.799毫秒。
defarr_sum_2(arr1,arr2):
res=arr1.copy()
fori,xinenumerate(arr2):
res[i]+=x
returnres
%timeitarr_sum_2(arr1,arr2)
799µs±16.7µsperloop(mean±std.dev.of7runs,1000loopseach)
4.4for x, y in zip
代替for range
避免arr[i]
的變數型別檢查帶來的額外開銷。平均執行時間0.769毫秒。
defarr_sum_3(arr1,arr2):
res=[]
forx,yinzip(arr1,arr2):
res.append(x+y)
returnres
%timeitarr_sum_3(arr1,arr2)
769µs±12.2µsperloop(mean±std.dev.of7runs,1000loopseach)
4.5 列表推導式套用zip
平均執行時間0.462毫秒。
defarr_sum_4(arr1,arr2):
return[x+yforx,yinzip(arr1,arr2)]
%timeitarr_sum_4(arr1,arr2)
462µs±3.43µsperloop(mean±std.dev.of7runs,1000loopseach)
5. 兩個列表相同元素的數量
輸入兩個列表,要求統計兩個列表相同元素的數量。其中每個列表內的元素都是不重複的。最終效能提升了5000倍。
首先建立兩個列表,並將元素的順序打亂。
fromrandomimportshuffle
arr1=list(range(2000))
shuffle(arr1)
arr2=list(range(1000,3000))
shuffle(arr2)
5.1 最常規的寫法
while迴圈巢狀,判斷元素arr1[i]
是否等於arr2[j]
,平均執行時間338毫秒。
defn_common_0(arr1,arr2):
res=0
i=0
m=len(arr1)
n=len(arr2)
whilei<m:
j=0
whilej<n:
ifarr1[i]==arr2[j]:
res+=1
j+=1
i+=1
returnres
%timeitn_common_0(arr1,arr2)
338ms±7.81msperloop(mean±std.dev.of7runs,1loopeach)
5.2for range
代替while迴圈
避免i += 1
的變數型別檢查帶來的額外開銷。平均執行時間233毫秒。
defn_common_1(arr1,arr2):
res=0
foriinrange(len(arr1)):
forjinrange(len(arr2)):
ifarr1[i]==arr2[j]:
res+=1
returnres
%timeitn_common_1(arr1,arr2)
233ms±10.9msperloop(mean±std.dev.of7runs,1loopeach)
5.3for x in arr
代替for range
避免arr[i]
的變數型別檢查帶來的額外開銷。平均執行時間84.8毫秒。
defn_common_2(arr1,arr2):
res=0
forxinarr1:
foryinarr2:
ifx==y:
res+=1
returnres
%timeitn_common_2(arr1,arr2)
84.8ms±1.38msperloop(mean±std.dev.of7runs,10loopseach)
5.4 使用if x in arr2
代替內層迴圈
平均執行時間24.9毫秒。
defn_common_3(arr1,arr2):
res=0
forxinarr1:
ifxinarr2:
res+=1
returnres
%timeitn_common_3(arr1,arr2)
24.9ms±1.39msperloop(mean±std.dev.of7runs,10loopseach)
5.4 使用更快的演算法
將陣列用.sort
方法排序,再進行單層迴圈遍歷。把時間複雜度從O(n2)
降低到O(nlogn)
,平均執行時間0.239毫秒。
defn_common_4(arr1,arr2):
arr1.sort()
arr2.sort()
res=i=j=0
m,n=len(arr1),len(arr2)
whilei<mandj<n:
ifarr1[i]==arr2[j]:
res+=1
i+=1
j+=1
elifarr1[i]>arr2[j]:
j+=1
else:
i+=1
returnres
%timeitn_common_4(arr1,arr2)
329µs±12.3µsperloop(mean±std.dev.of7runs,1000loopseach)
5.5 使用更好的資料結構
將陣列轉為集合,求交集的長度。平均執行時間0.067毫秒。
defn_common_5(arr1,arr2):
returnlen(set(arr1)&set(arr2))
%timeitn_common_5(arr1,arr2)
67.2µs±755nsperloop(mean±std.dev.of7runs,10000loopseach)