第 13 課 · 入門 · 18 分鐘

Python 列表推導式

用更緊湊的表達式從舊集合建構新集合,而不是寫一長串迴圈。

列表推導式篩選字典推導式

小黑板要給每款飲料都加上五毛錢的燕麥奶升級。迴圈能做,一行表達式讀起來卻像 Maya 會脫口而出的那句話。什麼時候推導式才是更清楚的選擇?

推導式可以用一個表達式,從可迭代物件建立新的列表、集合或字典。它不一定比普通迴圈更好;但當轉換很短、結果就是唯一目標時,推導式通常更清楚。 你還會看到推導式的作用域、巢狀 for 的閱讀順序,以及只能消費一次的生成器。

轉換每個元素

for 前面的表達式會應用到每個元素。結果是新列表,原列表不變。表達式要短到讀者一眼能看出輸入和輸出。

nums = [1, 2, 3]
doubled = [n * 2 for n in nums]
print(doubled)

映射值

用 if 篩選

末尾的 if 子句只保留通過測試的值。條件要放在 for 子句之後。如果需要多個分支,普通迴圈通常更好讀。

nums = [1, 2, 3, 4]
even = [n for n in nums if n % 2 == 0]
print(even)

篩選值

建構字典

字典推導式用冒號分隔鍵表達式和值表達式。需要同時拿到原鍵和值時,走訪 items()。

prices = {"latte": 4.5, "tea": 3.0}
expensive = {name: price for name, price in prices.items() if price >= 4}
print(expensive)

篩選字典

推導式的變數留在局部

Python 3 的推導式有自己的作用域。外面的 n 不會讀到推導式最後一次的值,這能避免變數洩漏。能帶出推導式的只有結果集合本身。

n = 99
squares = [n * n for n in range(4)]
print(squares)
print(n)

迴圈變數不會洩漏到外面

巢狀 for 從左往右讀

巢狀推導式就是把兩層迴圈壓成一行。第一個 for 是外層,第二個 for 對每個外層元素執行。篩選條件放在它所篩選的迴圈之後;順序不明顯時,改用具名迴圈。

rows = [[1, 2], [3, 4]]
flat = [value for row in rows for value in row]
pairs = [(row, value) for row in rows for value in row if value % 2 == 0]
print(flat)
print(pairs)

外層在前,篩選在後

生成器表達式只能消費一次

圓括號建立惰性生成器。sum() 會消費它;第二次再走訪,拿到的是已經耗盡的迭代器,所以結果是 0。需要反覆走訪就用列表,或者重新構造生成器。

numbers = (n * n for n in range(4))
print(sum(numbers))
print(sum(numbers))
print(list(n * n for n in range(4)))

流一次,或者把值存下來

本課要點

  • 推導式回傳一個新的集合。
  • for 子句提供值,if 子句負責篩選。
  • 字典推導式在 for 前使用鍵: 值。
  • 邏輯變得難讀時,改用普通迴圈。
  • 生成器只流一次;需要反覆走訪時用列表。

常見錯誤

  • 把 if 篩選寫在 for 子句前面。
  • 只用推導式產生 print 等副作用。
  • 寫出沒人能讀懂的複雜巢狀推導式。
  • 把同一個生成器交給兩個消費者,卻期待兩邊都看到全部元素。
輪到你

為 1 到 5 建構平方數列表,再只保留大於 10 的平方數。

動手試試

改改變數、先預測輸出,再執行程式碼。第一次點擊「執行程式碼」時會下載 Python 執行時,可能需要幾秒鐘。

瀏覽器內 Python本機執行 · 無需帳號
首次執行會從 CDN 下載 Pyodide。修改後點擊「執行程式碼」。

練習本課

12 道題,作答後立即看到解釋。

查看 12 道題答案開始練習
← 元組與集合排序與 key →