翻譯:《實用的Python程式設計》02_06_List_comprehension

codists發表於2021-02-25

目錄 | 上一節 (2.5 collections模組) | 下一節 (2.7 物件模型)

2.6 列表推導式

一個常見的任務是處理列表中的項(譯註:元素)。本節介紹列表推導式,完成此任務的強大工具。

建立新列表

列表推導式通過將操作應用於序列的每一個元素來建立新列表。

>>> a = [1, 2, 3, 4, 5]
>>> b = [2*x for x in a ]
>>> b
[2, 4, 6, 8, 10]
>>>

再如:

>>> names = ['Elwood', 'Jake']
>>> a = [name.lower() for name in names]
>>> a
['elwood', 'jake']
>>>

列表推導式的一般語法是:[ <expression> for <variable_name> in <sequence> ]

過濾

也可以在列表推導式中對元素進行過濾。

>>> a = [1, -5, 4, 2, -2, 10]
>>> b = [2*x for x in a if x > 0 ]
>>> b
[2, 8, 4, 20]
>>>

用例

列表推導式超級有用。例如,可以收集特定字典欄位的值:

stocknames = [s['name'] for s in stocks]

在序列上執行類資料庫查詢:

a = [s for s in stocks if s['price'] > 100 and s['shares'] > 50 ]

也可以把列表推導式與序列縮減合併在一起:

cost = sum([s['shares']*s['price'] for s in stocks])

一般語法

[ <expression> for <variable_name> in <sequence> if <condition>]

上面語法的含義:

result = []
for variable_name in sequence:
    if condition:
        result.append(expression)

歷史題外話

列表推導式來自於數學(集合構建符號)。

a = [ x * x for x in s if x > 0 ] # Python

a = { x^2 | x ∈ s, x > 0 }         # Math

這在其它幾種語言中也實現了,雖然大部分的程式設計師可能已經想不起他們的數學課了。所以,可以將其視為很酷的列表快捷方式。

練習

首先執行 report.py 程式,以便能夠在互動模式下中載入股票投資組合。

bash % python3 -i report.py

現在,在 Python 互動提示符下,輸入語句以執行下述操作。這些操作對投資組合資料執行各類縮減,轉換和查詢。

練習 2.19:列表推導式

嘗試一些簡單的列表推導式來熟悉語法:

>>> nums = [1,2,3,4]
>>> squares = [ x * x for x in nums ]
>>> squares
[1, 4, 9, 16]
>>> twice = [ 2 * x for x in nums if x > 2 ]
>>> twice
[6, 8]
>>>

請注意列表推導式是如何通過適當轉換或過濾的資料建立一個新列表的。

練習 2.20:序列縮減

使用單個 Python 語句計算投資組合的總價。

>>> portfolio = read_portfolio('Data/portfolio.csv')
>>> cost = sum([ s['shares'] * s['price'] for s in portfolio ])
>>> cost
44671.15
>>>

完成後,展示如何使用單個語句計算投資組合的當前值。

>>> value = sum([ s['shares'] * prices[s['name']] for s in portfolio ])
>>> value
28686.1
>>>

上面的兩個操作都是對映縮減的列子。列表推導式將操作對映到整個列表。

>>> [ s['shares'] * s['price'] for s in portfolio ]
[3220.0000000000005, 4555.0, 12516.0, 10246.0, 3835.1499999999996, 3254.9999999999995, 7044.0]
>>>

然後,sum() 函式對所有結果進行縮減。

>>> sum(_)
44671.15
>>>

有了這些知識,你現在就可以準備成立一家大資料創業公司了。

練習 2.21:資料查詢

請嘗試以下各種資料查詢示例。

首選是建立一個列表,儲存持有 100 股以上的股票投資組合。

>>> more100 = [ s for s in portfolio if s['shares'] > 100 ]
>>> more100
[{'price': 83.44, 'name': 'CAT', 'shares': 150}, {'price': 51.23, 'name': 'MSFT', 'shares': 200}]
>>>

持有 MSFT 和 IBM 股票的所有投資組合。

>>> msftibm = [ s for s in portfolio if s['name'] in {'MSFT','IBM'} ]
>>> msftibm
[{'price': 91.1, 'name': 'IBM', 'shares': 50}, {'price': 51.23, 'name': 'MSFT', 'shares': 200},
  {'price': 65.1, 'name': 'MSFT', 'shares': 50}, {'price': 70.44, 'name': 'IBM', 'shares': 100}]
>>>

持有總價超過 $10000 的所有股票投資組合。

>>> cost10k = [ s for s in portfolio if s['shares'] * s['price'] > 10000 ]
>>> cost10k
[{'price': 83.44, 'name': 'CAT', 'shares': 150}, {'price': 51.23, 'name': 'MSFT', 'shares': 200}]
>>>

練習 2.22:資料提取

展示如何構建元組 (name, shares) 列表,名稱(name )和 股數(shares)從股票投資組合(portfolio)中獲取:

>>> name_shares =[ (s['name'], s['shares']) for s in portfolio ]
>>> name_shares
[('AA', 100), ('IBM', 50), ('CAT', 150), ('MSFT', 200), ('GE', 95), ('MSFT', 50), ('IBM', 100)]
>>>

如果將方括號([,])更改為花括號({, }),那麼將得到集合推導式。這會得到獨一無二的的或無重複的值。

例如,這將確定集合中的股票名稱是獨一無二的:

>>> names = { s['name'] for s in portfolio }
>>> names
{ 'AA', 'GE', 'IBM', 'MSFT', 'CAT' }
>>>

如果指定鍵值對(key:value),則可以構建一個字典。例如,構建一個將股票名稱對映到持有的股票數量的字典:

>>> holdings = { name: 0 for name in names }
>>> holdings
{'AA': 0, 'GE': 0, 'IBM': 0, 'MSFT': 0, 'CAT': 0}
>>>

後面的特性就是眾所皆知的字典推導式。讓我們將其表格化:

>>> for s in portfolio:
        holdings[s['name']] += s['shares']

>>> holdings
{ 'AA': 100, 'GE': 95, 'IBM': 150, 'MSFT':250, 'CAT': 150 }
>>>

請嘗試以下示例,該示例將 prices 字典過濾出僅在 portfolio 中出現的名稱(name):

>>> portfolio_prices = { name: prices[name] for name in names }
>>> portfolio_prices
{'AA': 9.22, 'GE': 13.48, 'IBM': 106.28, 'MSFT': 20.89, 'CAT': 35.46}
>>>

練習 2.23: 從 CSV 檔案提取資料

在各類資料處理中,知道如何將列表,集合,字典推導式聯合使用會非常有用。這裡有一個示例,展示如何從 CSV 檔案中提取所選擇的列。

首先,從 CSV 檔案讀取一行標題資訊:

>>> import csv
>>> f = open('Data/portfoliodate.csv')
>>> rows = csv.reader(f)
>>> headers = next(rows)
>>> headers
['name', 'date', 'time', 'shares', 'price']
>>>

接著,定義一個變數列出實際需要的列:

>>> select = ['name', 'shares', 'price']
>>>

現在,在 CSV 原始檔中找到以上各列的索引。

>>> indices = [ headers.index(colname) for colname in select ]
>>> indices
[0, 3, 4]
>>>

最後,使用字典推導式讀取資料的一行並把其轉換為字典。

>>> row = next(rows)
>>> record = { colname: row[index] for colname, index in zip(select, indices) }   # dict-comprehension
>>> record
{'price': '32.20', 'name': 'AA', 'shares': '100'}
>>>

如果你對前面的操作感到滿意,那麼請讀取檔案的剩餘部分:

>>> portfolio = [ { colname: row[index] for colname, index in zip(select, indices) } for row in rows ]
>>> portfolio
[{'price': '91.10', 'name': 'IBM', 'shares': '50'}, {'price': '83.44', 'name': 'CAT', 'shares': '150'},
  {'price': '51.23', 'name': 'MSFT', 'shares': '200'}, {'price': '40.37', 'name': 'GE', 'shares': '95'},
  {'price': '65.10', 'name': 'MSFT', 'shares': '50'}, {'price': '70.44', 'name': 'IBM', 'shares': '100'}]
>>>

天啊,已經把 read_portfolio() 函式簡化為單個語句了。

說明

列表推導式在 Python 中常用作轉換,過濾和收集資料的有效方法。由於語法的原因,請不要走極端——應該讓每個列表推導式儘可能簡單。可以將事情分解為多個步驟。例如,不清楚你會不會把最後一個例子強加給毫不知情的同事。

也就是說,知道如何快速處理資料是一項非常有用的技能。在很多情況下,可能必須解決某種一次性的問題,包括資料匯入,匯出,提取等。成為列表推導式的大師可以大大減少設計方案所花費的時間。另外,不要忘記 collections 模組。

目錄 | 上一節 (2.5 collections模組) | 下一節 (2.7 物件模型)

注:完整翻譯見 https://github.com/codists/practical-python-zh

相關文章