1. 程式人生 > 其它 >為啥別人執行程式那麼快,而你的卻是龜速?

為啥別人執行程式那麼快,而你的卻是龜速?

技術標籤:pythonpython程式語言

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)

在這裡還是要推薦下我自己建的Python學習群:609616831,群裡都是學Python的,如果你想學或者正在學習Python ,歡迎你加入,大家都是軟體開發黨,不定期分享乾貨(只有Python軟體開發相關的),包括我自己整理的一份2020最新的Python進階資料和零基礎教學,歡迎進階中和對Python感興趣的小夥伴加入!