有效提升Python代碼性能的三個層面

使用python進入一個熟練的狀態之後就會思考提升代碼的性能,尤其是python的執行效率還有很大提升空間(委婉的說法)。面對提升效率這個話題,python自身提供了很多高性能模塊,很多大牛開發出了高效第三方包,可謂是百花齊放。下面根據我個人使用總結出提升性能的幾個層面和相關方法。

python代碼優化:

  1. 語法層面
  2. 高效模塊
  3. 解釋器層面

語法層面

  1. 變量定義
  2. 數據類型
  3. 條件判斷
  4. 循環
  5. 生成器

變量定義

  1. 多使用局部變量少使用全局變量,命名空間中局部變量優先搜索

條件判斷

  1. 可以使用字典的key value特性,直接用key命中條件,避免if判斷
  2. 用in操作替換if else判斷
  3. 使用any 或 all 將多個判斷一起處理,減少if else的分支
  4. if條件的短路特性。if a or b這種判斷中,如果a是True就不會判斷b,所以將True條件寫在前面可以節省判斷時間。同理 and 判斷將假寫在前面,後面一個條件不判斷

數據類型

  1. 使用dict 或set查找,替換list或tuple
  2. 集合的交併補差操作效率非常高。for循環和集合都可以處理的選擇集合解決,集合的效率遠高於循環

循環

  1. 用for循環代替while循環,for循環比while循環快
  2. 使用隱式for循環代替顯式for循環。如sum,map,filter,reduce等都是隱式for循環。隱式循環快於顯式循環
  3. 儘量不要打斷循環。打斷循環的放在外面。有判斷條件的語句和與循環不相關的操作語句儘量放在for外面
  4. 應當將最長的循環放在最內層,最短的循環放在最外層,以減少CPU跨切循環層的次數
  5. 使用生成式替換循環創建

合理使用迭代器和生成器

需要迭代出大量數據的場景,不需要將所有數據創建出來,合理使用生成器減少內存消耗

items_gen = (i for i in range(5000))
>>> items_gen.__sizeof__()
96
items_list = [i for i in ragne(5000)]
>>> items_list.__sizeof__()
43016

高效模塊

  1. collections 數據增強模塊
  2. itertools 高效迭代模塊
  3. array 高效數組
  4. functool 用於處理函數的高階函數包

collections

  1. Counter: 高效的統計庫
  2. defaultdict:帶默認值的字典
  3. ChainMap:高效組合字典的庫
  4. deque: 雙端隊列,高效插入刪除

詳細使用參見另一篇專門講collections的文章 Python原生數據結構增強模塊collections

itertools

  1. chain:多個可迭代對象構建成一個新的可迭代對象
  2. groupby:按照指定的條件分類,輸出條件和符合條件的元素
  3. from_iteratorable:一個迭代對象中將所有元素類似於chain一樣,統一返回
  4. islice:對迭代器進行切片,能指定start和stop以及步長

詳細使用參見另一篇專門講itertools的文章Python高性能工具迭代標準庫itertools

array

array 模塊是python中實現的一種高效的數組存儲類型。
它和list相似,但是所有的數組成員必須是同一種類型,在創建數組的時候,就確定了數組的類型。

functool

functools.lru_cache 對函數做緩存

lru_cache 是一個裝飾器,爲函數提供緩存功能。被裝飾的函數以相同參數調用時直接返回上一次的結果。
不做緩存

import time
 
def fibonacci(n):
    """斐波那契函數"""
    if n < 2:
        return n
    return fibonacci(n - 2) + fibonacci(n - 1)


start = time.time()
res = fibonacci(40)
end = time.time()
print(res)
print(end - start)
102334155
32.14816737174988

做緩存

import time
from functools import lru_cache      
 
@lru_cache
def fibonacci(n):
    """斐波那契函數"""
    if n < 2:
        return n
    return fibonacci(n - 2) + fibonacci(n - 1)


start = time.time()
res = fibonacci(40)
end = time.time()
print(res)
print(end - start)

102334155
0.00020623207092285156

使用注意:

  1. 緩存是按照參數作爲鍵。調用函數時任意一個參數發生變化都不會返回之前緩存結果
  2. 所有參數必須可哈希hash。也就是說參數只能是不可變對象

解釋器層面:

減少python執行過程

python 代碼的執行過程爲:

  1. 編譯器將源碼編譯成中間狀態的字節碼
  2. 解釋器執行字節碼,將字節碼轉成機器碼在cpu上運行

python慢的原因主要是因爲解釋器。解決辦法有兩個:
一是解決辦法是使用C/C++語言重寫Python函數,但是這要求程序員對C/C++語言熟悉,且調試速度慢,不適合絕大多數Python程序員。
另外一種非常方便快捷的解決辦法就是使用Just-In-Time(JIT)技術。

Just-In-Time(JIT)技術爲解釋語言提供了一種優化,它能克服上述效率問題,極大提升代碼執行速度,同時保留Python語言的易用性。使用JIT技術時,JIT編譯器將Python源代碼編譯成機器直接可以執行的機器語言,並可以直接在CPU等硬件上運行。這樣就跳過了原來的虛擬機,執行速度幾乎與用C語言編程速度並無二致。

Numba是一個針對Python的開源JIT編譯器,由Anaconda公司主導開發,可以對Python原生代碼進行CPU和GPU加速。

import time

def fun(x):
    total = 0
    start = time.time()
    for i in range(1,x+1):
        total += i 
    end = time.time()
    print(total)
    print(end - start)

fun(100000000)
5000000050000000
5.934630393981934
import time
from numba import jit, int32

@jit(int32(int32))
def fun(x):
    total = 0
    start = time.time()
    for i in range(1,x+1):
        total += i 
    end = time.time()
    print(total)
    print(end - start)

fun(100000000)
5000000050000000
0.1186532974243164

速度有60倍提升

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章