Python進階系列:細講Python推導式

Python 泛函編程 編程語言 雷神之錘 卡門的兒子 2019-06-11
Python進階系列:細講Python推導式

前言

由於Python的良好生態,很多時候我們的程序只是通過調用別人寫好的方法即可實現功能。

不過,很多時候我們還是需要處理序列。不管是入門中還是早已入門的小夥伴,對於處理序列毫無疑問會選擇用for循環。

在Python中還有一種更高效更簡潔的處理序列方式——推導式。本文詳細探討關於推導式的細節。

本文重點

  • 學習推導式
  • 推導式的優缺點
  • 如何合理使用推導式
  • 推導式可能的進化

本文字數2000+,適合已入門Python和希望水平有所提升的小夥伴。

原創不容易,請點擊右上方關注按鈕,多多支持~

for循環有啥不好,非要學推導式?

我們來看一個例子,如何把一個數值列表中大於0的數值篩選出來。下圖給出for循環的做法

Python進階系列:細講Python推導式

可以看到for循環還是妥妥地把問題解決,代碼並不複雜。 分析代碼與原問題的表達對應關係:

  • 行5,表達從數值列表取出數值。
  • 行6,表達"大於0的數值篩選出來"
  • 但原問題沒有提及到創建一個用於保存結果的列表和如何把結果加入結果列表。
  • 上面的代碼中的行4與行7,都是多餘的動作。

是時候讓推導式出場了

Python進階系列:細講Python推導式

上圖是一種比較"官方"的寫法,把整個推導式寫到一行裡,我更喜歡下圖的寫法。

Python進階系列:細講Python推導式

這就是列表推導式,很簡單吧。看起來其實與之前的for循環寫法差不多。但推導式有以下好處。

  • 不需要像for循環那樣,先定義一個列表,然後在循環中編寫如何把結果放入列表的代碼。
  • 表達更為清晰了,推導式的每個部分都與原問題的表達一一對應
  • 行15,表達了 我要把什麼樣的東西放入結果中,這裡只有一個n,表示符合要求的數值。
  • 行16與行17與之前for循環分析是一致。注意看,這裡不再需要寫冒號了。
  • 推導式的外面用一個[]包圍著,表示結果是一個列表。
  • 推導式的性能更好。在序列的數據量不大的情況下推導式的性能優勢不會太明顯,如果序列的元素數量成千上萬,那麼推導式比for形式性能通常優勝2倍以上。

通過對比學習推導式

覺得怎麼樣,推導式是不是語義表達好的同時性能又高呢。下面通過與for循環形式對比來學習推導式的寫法。 圖中左邊是for循環,右邊是推導式

Python進階系列:細講Python推導式

  • 紅框部分表示遍歷序列,可以看到兩者形式一樣,但注意,推導式不需要在最後寫冒號
Python進階系列:細講Python推導式

  • 同樣地,上圖紅框表示如何判斷每個元素,這裡表示過濾的條件。
  • 我們可以寫各種各樣複雜的判斷條件。
Python進階系列:細講Python推導式

  • 上圖紅框是推導式最後一部分,他決定了輸出結果
  • 比如說,如果希望每個輸出值是原來的兩倍,我們就可以寫 n*2
  • 結果可以是各種各樣的類型,比如紅框部分如果寫 f'值:{n}',那麼結果就是一系列的字符串。

更進一步

我們來看一個稍微複雜點的例子。 假設我們有多個文件,每個文件都有多行數值(都是整數),行數不確定。如圖:

Python進階系列:細講Python推導式

現在需要把多個這樣的文件的所有數值拿出來,然後把小於50的數值篩選出來作為結果,並且標註每個數值的來源文件。 下圖是基本數據的定義

Python進階系列:細講Python推導式

  • 方法 get_nums_from_file 不是這裡的重點,我們只需要知道,給他一個文本路徑,他會讀取文件中的每行的整數,以返回一個整數列表

這個問題可以描述為"列表中的元素還可以提取出一個列表",這樣的情況下同樣可以用推導式。如下:

Python進階系列:細講Python推導式

  • 其實與普通的for循環嵌套是差不多的套路
  • 行38,首先遍歷paths列表
  • 行39,在上一個循環中獲取文件中的整數列表再次遍歷這個整數列表
  • 行40,對應原問題的篩選條件。
  • 行37,這裡可以使用下方兩個for的變量f和n,因此可以輕而易舉找到每個數值的來源

有時候不應該強行使用推導式

我們很容易犯的一個錯誤是,手上拿著一個錘子,看啥都認為是釘子,更何況拿著的是一個雷神之錘。

推導式簡潔又高效的好處,很容易讓人著迷於使用他來解決一切的集合處理問題。我們接著上面的需求來說明。 現在需求不僅僅過濾小於50的數值,而是取出"小於所在文件的所有數值的平均",並且結果需要顯示該文件的平均值。 下面是推導式的解決方法:

Python進階系列:細講Python推導式

  • 推導式最大的問題在於無法在過程中建立臨時變量
  • 這個需求下,由於沒法用臨時變量保存一個文件的平均值,因此導致多次求平均,不僅代碼結構混亂,而且效率還很低。

這時候老老實實使用for循環是個很好的選擇。如下:

Python進階系列:細講Python推導式

未來,推導式可能的進化

Python的推導式其實來源於函數式編程中的思想,目前市面上的幾門面向對象編程語言都加入了相關方面的語法,未來Python的推導式可能會參考他們從而改進自身的推導式語法。如圖為C#的Linq,特點是他允許在過程中定義臨時變量。

Python進階系列:細講Python推導式

可以看到,如果Python的推導式加入這樣的語法功能,那麼本文說的推導式的缺點就不再出現。Python的推導式在未來的進化值得期待。

小結

  • 在處理序列時,推導式是一個高效簡潔的方式
  • 當需求需要在循環中創建各種臨時的狀態數據時,推導式就不再適合處理。建議考慮使用for循環。

在Python中,推導式很多時候被當作是否熟悉Python的標誌之一,同時推導式也存在許多爭議,我們應該清楚瞭解推導式再談如何應用,畢竟任何技術都必需在適當的地方才能發揮最大的作用。

本文示例的代碼:https://github.com/CrystalWindSnake/Creative/tree/master/python/comprehensions

你已經學會了推導式了嗎?覺得使用for循環還是推導式比較好呢?

如果覺得本文對你有所幫助,記得關注、評論、轉發、收藏噢~

私信我"python",即可獲得按水平領域分類好的Python資料,還會不定期更新有用的Python技巧。

相關推薦

推薦中...