搜尋
首頁後端開發Python教學20招讓你的Python飛起來

今天分享的這篇文章,文字不多,程式碼為主。絕對是乾貨,童叟無欺,主要分享了提升 Python 效能的 20 個技巧,教你如何告別慢Python。原文作者 開元,全端程式設計師,使用 Python, Java, PHP和C++。

1. 最佳化演算法時間複雜度

演算法的時間複雜度對程式的執行效率影響最大,在Python中可以透過選擇合適的數據結構來最佳化時間複雜度,如list和set找出某一個元素的時間複雜度分別是O(n)和O(1)。不同的場景有不同的最佳化方式,總得來說,一般有分治,分支界限,貪心,動態規劃等思想。

2. 減少冗餘資料

如用上三角或下三角的方式去保存一個大的對稱矩陣。在0元素佔大多數的矩陣裡使用稀疏矩陣表示。

3. 合理使用copy與deepcopy

對於dict和list等資料結構的對象,直接賦值使用的是引用的方式。而有些情況下需要複製整個對象,這時可以使用copy包裡的copy和deepcopy,這兩個函數的不同之處在於後者是遞歸複製的。效率也不一樣:(以下程式在ipython中運行)

import copy
a = range(100000)
%timeit -n 10 copy.copy(a) # 运行10次 copy.copy(a)

%timeit -n 10 copy.deepcopy(a)
10 loops, best of 3: 1.55 ms per loop
10 loops, best of 3: 151 ms per loop

#timeit後面的-n表示運行的次數,後兩行對應的是兩個timeit的輸出,下同。由此可見後者慢一個數量級。

4. 使用dict或set查找元素

Python dict和set都是使用hash表來實作(類似c++11標準函式庫中unordered_map),查找元素的時間複雜度是O(1)。

a = range(1000)
s = set(a)
d = dict((i,1) for i in a)
%timeit -n 10000 100 in d
%timeit -n 10000 100 in s
10000 loops, best of 3: 43.5 ns per loop
10000 loops, best of 3: 49.6 ns per loop

dict的效率略高(佔用的空間也多一些)。

5. 合理使用生成器(generator)和yield

#
%timeit -n 100 a = (i for i in range(100000))
%timeit -n 100 b = [i for i in range(100000)]
100 loops, best of 3: 1.54 ms per loop
100 loops, best of 3: 4.56 ms per loop

使用()得到的是一個generator對象,所需的記憶體空間與列表的大小無關,所以效率會高一些。在具體應用上,例如set(i for i in range(100000))會比set([i for i in range(100000)])快。

但對於需要循環遍歷的情況:

%timeit -n 10 for x in (i for i in range(100000)): pass
%timeit -n 10 for x in [i for i in range(100000)]: pass

10 loops, best of 3: 6.51 ms per loop
10 loops, best of 3: 5.54 ms per loop

後者的效率反而更高,但如果循環裡有break,用generator的好處是顯而易見的。 yield也是用於創建generator:

def yield_func(ls):
  for i in ls:
    yield i+1

def not_yield_func(ls):
  return [i+1 for i in ls]

ls = range(1000000)
%timeit -n 10 for i in yield_func(ls):pass

%timeit -n 10 for i in not_yield_func(ls):pass

10 loops, best of 3: 63.8 ms per loop
10 loops, best of 3: 62.9 ms per loop

#對於記憶體不是非常大的list,可以直接回傳一個list,但是可讀性yield更佳(人個喜好)。
python2.x內建generator功能的有xrange函式、itertools套件等。

6. 優化循環

循環之外能做的事不要放在循環內,例如下面的優化可以快一倍:

a = range(10000)
size_a = len(a)
%timeit -n 1000 for i in a: k = len(a)
%timeit -n 1000 for i in a: k = size_a
1000 loops, best of 3: 569 µs per loop
1000 loops, best of 3: 256 µs per loop

7. 最佳化包含多個判斷表達式的順序

對於and,應該把滿足條件少的放在前面,對於or,把滿足條件多的放在前面。如:

a = range(2000) 
%timeit -n 100 [i for i in a if 10 < i < 20 or 1000 < i < 2000]
%timeit -n 100 [i for i in a if 1000 < i < 2000 or 100 < i < 20]   
%timeit -n 100 [i for i in a if i % 2 == 0 and i > 1900]
%timeit -n 100 [i for i in a if i > 1900 and i % 2 == 0]
100 loops, best of 3: 287 µs per loop
100 loops, best of 3: 214 µs per loop
100 loops, best of 3: 128 µs per loop
100 loops, best of 3: 56.1 µs per loop

8. 使用join合併迭代器中的字串

In [1]: %%timeit
  ...: s = &#39;&#39;
  ...: for i in a:
  ...:     s += i
  ...:
10000 loops, best of 3: 59.8 µs per loop

In [2]: %%timeit
s = &#39;&#39;.join(a)
  ...:
100000 loops, best of 3: 11.8 µs per loop

join對於累加的方式,有大約5倍的提升。

9. 選擇適當的格式化字元方式

s1, s2 = &#39;ax&#39;, &#39;bx&#39;

%timeit -n 100000 &#39;abc%s%s&#39; % (s1, s2)
%timeit -n 100000 &#39;abc{0}{1}&#39;.format(s1, s2)
%timeit -n 100000 &#39;abc&#39; + s1 + s2
100000 loops, best of 3: 183 ns per loop
100000 loops, best of 3: 169 ns per loop
100000 loops, best of 3: 103 ns per loop

三種情況中,%的方式是最慢的,但是三者的差距並不大(都非常快)。 (個人覺得%的可讀性最好)

10. 不借助中間變數交換兩個變數的值

##

In [3]: %%timeit -n 10000
  a,b=1,2
  ....: c=a;a=b;b=c;
  ....:
10000 loops, best of 3: 172 ns per loop

In [4]: %%timeit -n 10000

a,b=1,2a,b=b,a
  ....:
10000 loops, best of 3: 86 ns per loop

使用a,b=b,a而不是c=a;a=b;b=c;來交換a,b的值,可以快1倍以上。

11. 使用if is

a = range(10000)
%timeit -n 100 [i for i in a if i == True]
%timeit -n 100 [i for i in a if i is True]
100 loops, best of 3: 531 µs per loop
100 loops, best of 3: 362 µs per loop

使用 if is True 比 if == True 將近一倍。

12. 利用級聯比較x

#

x, y, z = 1,2,3

%timeit -n 1000000 if x < y < z:pass
%timeit -n 1000000 if x < y and y < z:pass

1000000 loops, best of 3: 101 ns per loop
1000000 loops, best of 3: 121 ns per loop

x

13. while 1 比while True 更快

#

def while_1():
  n = 100000
  while 1:
    n -= 1
    if n <= 0: break

def while_true():
  n = 100000
  while True:
    n -= 1
    if n <= 0: break

m, n = 1000000, 1000000

%timeit -n 100 while_1()
%timeit -n 100 while_true()
100 loops, best of 3: 3.69 ms per loop
100 loops, best of 3: 5.61 ms per loop

while 1 比while true快很多,原因是在python2.x中,True是一個全域變量,而非關鍵字。

14. 使用**而不是pow

#

%timeit -n 10000 c = pow(2,20)
%timeit -n 10000 c = 2**20

10000 loops, best of 3: 284 ns per loop
10000 loops, best of 3: 16.9 ns per loop

**就是快10倍以上!

15. 使用cProfile, cStringIO 和cPickle等用c實作相同功能(分別對應profile, StringIO, pickle)的套件

#

import cPickle
import pickle
a = range(10000)
%timeit -n 100 x = cPickle.dumps(a)
%timeit -n 100 x = pickle.dumps(a)
100 loops, best of 3: 1.58 ms per loop
100 loops, best of 3: 17 ms per loop

由c實現的包,速度快10倍以上!

16. 使用最佳的反序列化方式

下面比較了eval, cPickle, json方式三種對對應字串反序列化的效率:

import json
import cPickle
a = range(10000)
s1 = str(a)
s2 = cPickle.dumps(a)
s3 = json.dumps(a)
%timeit -n 100 x = eval(s1)
%timeit -n 100 x = cPickle.loads(s2)
%timeit -n 100 x = json.loads(s3)
100 loops, best of 3: 16.8 ms per loop
100 loops, best of 3: 2.02 ms per loop
100 loops, best of 3: 798 µs per loop

可見json比cPickle快近3倍,比eval快20倍以上。

17. 使用C擴充(Extension)

目前主要有CPython(python最常見的實作的方式)原生API, ctypes,Cython,cffi三種方式,它們的作用是使得Python程式可以呼叫由C編譯成的動態連結函式庫,其特點分別是:

CPython原生API: 透過引入Python.h頭文件,對應的C程式中可以直接使用Python的資料結構。實作過程相對繁瑣,但有比較大的適用範圍。
ctypes: 通常用於封裝(wrap)C程序,讓純Python程序呼叫動態連結函式庫(Windows中的dll或Unix中的so檔)中的函數。如果想要在python中使用已經有C類別函式庫,使用ctypes是很好的選擇,有一些基準測試下,python2+ctypes是效能最好的方式。
Cython: Cython是CPython的超集,用來簡化編寫C擴充的過程。 Cython的優點是語法簡潔,可以很好地相容於numpy等包含大量C擴充的函式庫。 Cython的使得場景一般是針對專案中某個演算法或過程的最佳化。在某些測試中,可以有幾百倍的效能提升。
cffi: cffi的就是ctypes在pypy(詳見下文)中的實現,同進也相容於CPython。 cffi提供了在python使用C類別函式庫的方式,可以直接在python程式碼中編寫C程式碼,同時支援連結到現有的C類別庫。
使用這些最佳化方式一般是針對已有專案效能瓶頸模組的最佳化,可以在少量改動原有專案的情況下大幅提高整個程式的運作效率。

18. 平行程式設計

因為GIL的存在,Python很難充分利用多核心CPU的優勢。但是,可以透過內建的模組multiprocessing實現下面幾種並行模式:

#多進程:#對於CPU密集型的程序,可以使用multiprocessing的Process,Pool等封裝好的類,透過多進程的方式實現並行計算。但因為進程中的通訊成本比較大,對於進程之間需要大量資料互動的程式效率未必有大的提升。
多執行緒:對於IO密集型的程序,multiprocessing.dummy模組使用multiprocessing的介面封裝threading,使得多執行緒程式設計也變得非常輕鬆(例如可以使用Pool的map接口,簡潔高效)。
分散式:multiprocessing中的Managers類別提供了可以在不同進程之共享資料的方式,可以在此基礎上開發出分散式的程式。
不同的業務場景可以選擇其中的一種或幾種的組合實現程式效能的最佳化。

19. 終級大殺器:PyPy

PyPy是用RPython(CPython的子集)實現的Python,根據官網的基準測試數據,它比CPython實現的Python快6倍以上。快的原因是使用了Just-in-Time(JIT)編譯器,即動態編譯器,與靜態編譯器(如gcc,javac等)不同,它是利用程式運行的過程的資料進行最佳化。由於歷史原因,目前pypy中還保留著GIL,不過正在進行的STM計畫試圖將PyPy變成沒有GIL的Python。

如果python程式中含有C擴充(非cffi的方式),JIT的最佳化效果會大打折扣,甚至比CPython慢​​(比Numpy)。所以在PyPy中最好用純Python或使用cffi擴展。

隨著STM,Numpy等項目的完善,相信PyPy將會取代CPython。

20. 使用效能分析工具

除了上面在ipython使用到的timeit模組,還有cProfile。 cProfile的使用方式也非常簡單:python -m cProfile filename.py,filename.py 是要執行程式的檔案名,可以在標準輸出中看到每一個函數被呼叫的次數和運行的時間,從而找到程式的效能瓶頸,然後可以有針對性地最佳化。

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持PHP中文網。

更多20招讓你的Python飛起來相關文章請關注PHP中文網!

陳述
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
Python:自動化,腳本和任務管理Python:自動化,腳本和任務管理Apr 16, 2025 am 12:14 AM

Python在自動化、腳本編寫和任務管理中表現出色。 1)自動化:通過標準庫如os、shutil實現文件備份。 2)腳本編寫:使用psutil庫監控系統資源。 3)任務管理:利用schedule庫調度任務。 Python的易用性和豐富庫支持使其在這些領域中成為首選工具。

Python和時間:充分利用您的學習時間Python和時間:充分利用您的學習時間Apr 14, 2025 am 12:02 AM

要在有限的時間內最大化學習Python的效率,可以使用Python的datetime、time和schedule模塊。 1.datetime模塊用於記錄和規劃學習時間。 2.time模塊幫助設置學習和休息時間。 3.schedule模塊自動化安排每週學習任務。

Python:遊戲,Guis等Python:遊戲,Guis等Apr 13, 2025 am 12:14 AM

Python在遊戲和GUI開發中表現出色。 1)遊戲開發使用Pygame,提供繪圖、音頻等功能,適合創建2D遊戲。 2)GUI開發可選擇Tkinter或PyQt,Tkinter簡單易用,PyQt功能豐富,適合專業開發。

Python vs.C:申請和用例Python vs.C:申請和用例Apr 12, 2025 am 12:01 AM

Python适合数据科学、Web开发和自动化任务,而C 适用于系统编程、游戏开发和嵌入式系统。Python以简洁和强大的生态系统著称,C 则以高性能和底层控制能力闻名。

2小時的Python計劃:一種現實的方法2小時的Python計劃:一種現實的方法Apr 11, 2025 am 12:04 AM

2小時內可以學會Python的基本編程概念和技能。 1.學習變量和數據類型,2.掌握控制流(條件語句和循環),3.理解函數的定義和使用,4.通過簡單示例和代碼片段快速上手Python編程。

Python:探索其主要應用程序Python:探索其主要應用程序Apr 10, 2025 am 09:41 AM

Python在web開發、數據科學、機器學習、自動化和腳本編寫等領域有廣泛應用。 1)在web開發中,Django和Flask框架簡化了開發過程。 2)數據科學和機器學習領域,NumPy、Pandas、Scikit-learn和TensorFlow庫提供了強大支持。 3)自動化和腳本編寫方面,Python適用於自動化測試和系統管理等任務。

您可以在2小時內學到多少python?您可以在2小時內學到多少python?Apr 09, 2025 pm 04:33 PM

兩小時內可以學到Python的基礎知識。 1.學習變量和數據類型,2.掌握控制結構如if語句和循環,3.了解函數的定義和使用。這些將幫助你開始編寫簡單的Python程序。

如何在10小時內通過項目和問題驅動的方式教計算機小白編程基礎?如何在10小時內通過項目和問題驅動的方式教計算機小白編程基礎?Apr 02, 2025 am 07:18 AM

如何在10小時內教計算機小白編程基礎?如果你只有10個小時來教計算機小白一些編程知識,你會選擇教些什麼�...

See all articles

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

AI Hentai Generator

AI Hentai Generator

免費產生 AI 無盡。

熱門文章

R.E.P.O.能量晶體解釋及其做什麼(黃色晶體)
4 週前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳圖形設置
4 週前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您聽不到任何人,如何修復音頻
1 個月前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.聊天命令以及如何使用它們
1 個月前By尊渡假赌尊渡假赌尊渡假赌

熱工具

Atom編輯器mac版下載

Atom編輯器mac版下載

最受歡迎的的開源編輯器

MinGW - Minimalist GNU for Windows

MinGW - Minimalist GNU for Windows

這個專案正在遷移到osdn.net/projects/mingw的過程中,你可以繼續在那裡關注我們。 MinGW:GNU編譯器集合(GCC)的本機Windows移植版本,可自由分發的導入函式庫和用於建置本機Windows應用程式的頭檔;包括對MSVC執行時間的擴展,以支援C99功能。 MinGW的所有軟體都可以在64位元Windows平台上運作。

EditPlus 中文破解版

EditPlus 中文破解版

體積小,語法高亮,不支援程式碼提示功能

Dreamweaver Mac版

Dreamweaver Mac版

視覺化網頁開發工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器