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


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

python代碼優化:

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

語法層面

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

變量定義

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

條件判斷

  1. 可以使用字典的key value特性,直接用key命中條件,避免if判斷
  2. 用in操作在判斷是否存在方面替換if else判斷
  3. 用max,min等內置函數在判斷大小方面可以替換if else
  4. 用bool可以判斷出True或False,結合int(bool(object))可以在判斷真值方面替換if else
  5. 使用any 或 all 將多個判斷一起處理,減少if else的分支
  6. 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 用於處理函數的高階函數包
  5. 異步編程相關模塊

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。也就是說參數只能是不可變對象

異步編程相關模塊

自從python3.6之后,異步編程的思想逐漸成熟。異步編程在IO密集性任務中可以非常有效的提升程序效率。
異步編程用做客戶端可以提高網絡請求的並發量,如aiohttp異步請求的模塊
異步編程用戶服務端可以提高網絡請求的處理速度,比較知名的web異步編程框架有:

  1. Tornado 老牌的異步編程框架
  2. Fastapi 當下最火熱的異步編程框架
  3. Sanic 速度最快的異步編程框架

異步編程用於文件讀寫的模塊,如aiofiles

異步編程是一個巨大的話題,限於篇幅另開一系列來介紹。

解釋器層面:

減少python執行過程

python 代碼的執行過程為:

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

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

JIT技術

一是解決辦法是使用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倍提升

更換解釋器

python默認使用的解釋器是Cpython,特點是將python代碼編譯成C語言執行。Cython有一個很大的問題就是大名鼎鼎的GIL,全局解釋器鎖。
除了Cpython之外,還可以選擇的包括:

  1. Jython:將python代碼編譯為 Java 字節碼,從而做到跨平台
  2. Pyston :Pyston 是 CPython 解釋器的一個分支,它實現了性能優化。
  3. Codon:一種高性能的 Python 編譯器,可將 Python 代碼編譯為本機機器代碼,而無需任何運行時開銷

總結

提高python性能是一個巨大的主題,需要對python編程多思考多琢磨。這是一個有趣的主題,我相信在這個主題上投入的性價比也會很高。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM