翻譯 | 更快的Python(二)
更快的Python(Python Faster Way)使用程式碼示例來說明如何書寫Python程式碼能帶來更高的效能。本文對程式碼進行了講解,從效能和可讀性等角度來選擇出最適合的寫法。
例子11:字串連線

- 最差/最優時間比: 1.15
- 使用建議:一次性連線多個(3個以上)的字串的時候,使用join,其他情況使用加號或f-string。
- 說明:又是一個字串連線的問題,不過這個例子舉的不好,join適用的場景是一次連線多個字串,會比加號連線多個字串要快很多(加號相當於一個一個連線)。
例子12:數字的格式化

- 最差/最優時間比: 1.29
- 使用建議:需要複雜格式,推薦使用format方法;將數字轉換為字串,直接使用str方法。
- 說明:將數字轉為字串,使用str方法要快於format方法,因為format方法支援在轉換過程中增加規則,例如將數字轉為貨幣形式(每三位加一個逗號分隔符)。
例子13:獲取內建列表型別的長度

- 最差/最優時間比: 1.20
- 使用建議:使用len()方法。
- 說明:當呼叫len()方法時,系統實際上是呼叫了物件內建的 len 方法,從這個層面理解,直接呼叫 len 應該比len()方法更快。但是當len()內建的列表方法時,Python直譯器做了優化,直接返回了列表物件中儲存長度資訊的變數,並不會呼叫 len 。
例子14:整數型別的運算

- 最差/最優時間比: 2.63
- 使用建議:不要直接呼叫 add 等魔術方法。
- 說明:對於整數型別,呼叫魔術方法完成運算的速度遠遠慢於直接使用運算子,使用運算子時,Python直譯器直接呼叫C實現的operaotr包中的運算方法,所以速度很快;而使用呼叫魔術方法,在Python層面多出了呼叫 add 等魔術方法的額外操作。
例子15:自定義型別的運算子過載

- 最差/最優時間比: 1.06
- 使用建議:不要直接呼叫 add 等魔術方法。
- 說明:對於過載了運算子的物件,沒有對應的C實現運算方法,所以直接直接呼叫魔術方法速度會更快。
例子16:對range結果求和

- 最差/最優時間比: 2.95
- 使用建議:推薦使用第一種。
- 說明:和第一種相比,第三種會遍歷range先生成一個列表,然後將列表傳給sum,速度最慢,而第一種直接傳遞迭代器給sum,省去了遍歷生成列表的過程;第二種和第一種相比則是在Python層面實現了求和,而sum是C層面實現的求和,所以也沒有第一種塊。
例子17:for迴圈和表示式構建列表的區別

- 最差/最優時間比: 2.05
- 使用建議:推薦使用表示式構建。
- 說明:兩種方式看上去邏輯一樣,都是把range迭代器遍歷,生成一個列表,但是表示式是在位元組碼層面構建了一個迴圈來生成,而第二種則是在Python層面建立列表,並不斷Append,效能上要差於第一種。
例子18:for迴圈和表示式構建字典的區別

- 最差/最優時間比: 1.49
- 使用建議:推薦使用表示式。
- 說明:dict的update方法適用於合併兩個字典的情況,也就是說可以一次合併多個key,所以相比於直接訪問key速度要慢;根據圖中的測試,在100這個量級上,表示式生成的速度要慢一些,但是在更大的量級上,表示式的優勢就體現出來了,並且更加Pythonic。首先表示式方法是在位元組碼層面生成迴圈的,所以理論上比Python層面生成迴圈構建字典要快的,那麼為什麼在小量級的場景下,位元組碼反倒沒有優勢呢?根據dis出的位元組碼可以看到,表示式構建首先會MAKE_FUNCTION然後再CALL_FUNCTION,這裡會有一些基本的消耗,量級小的時候,這些基本消耗佔比高,量級越大,這些基本消耗所佔比例就越低,表示式方法的優勢也就越明顯。
例子19:for迴圈和表示式構建字典的區別

- 最差/最優時間比: 2.89
- 使用建議:推薦使用表示式構建。
- 說明:理由同上一個例子。
例子20:轉換為bool值

- 最差/最優時間比: N/A
- 使用建議:根據具體情況選擇。
- 說明:這個比較似乎沒有什麼好說的,時間的區別主要原因是構建a物件的成本不同。
參考文章
- ofollow,noindex">Python Faster Way