1. 程式人生 > 程式設計 >Python 列表推導式需要注意的地方

Python 列表推導式需要注意的地方

原文地址:The Do's and Don'ts of Python List Comprehension
原文作者:Yong Cui,Ph.D.
譯文出自:掘金翻譯計劃
本文永久連結:github.com/xitu/gold-m…
譯者:samyu2000
校對者:luochen1992,shixi-li

Python 列表推導式並不是給初學者用的,因為它非常反直覺,甚至對於有其他程式語言背景的人也是如此。

我們接觸到 List 的使用時,學習的內容都是零散的。所以我們缺少一個關於如何在各種各樣的場景下使用 List 的知識體系。

本文提供了一些 List 的使用指南,儘可能涵蓋各個方面。希望本文可以成為你的一站式實用手冊。

使用建議

1.建議使用迭代的方式

使用 List 最基本的方式是以一個可迭代物件為基礎,建立一個 List 物件,這個可迭代物件可以是任意可以迭代元素的Python物件。使用方法如下。

[expression for item in iterable]

下面這段程式碼展示了一個使用列表相關技術建立 List 物件的例子。在這個例子中,我們定義了一個 Integer 列表,並基於這個物件建立了儲存每個數字的平方數和立方數的 List 物件。

>>> # 建立一個 Integer 列表
>>> integers = [1,2,3,4,5,6]
>>> # 建立平方數和立方數列表
>>> powers = [(x*x,pow(x,3)) for x in integers]
>>> print(powers)
[(1,1),(4,8),(9,27),(16,64),(25,125),(36,216)]

上面的例子把 List 物件當作迭代器使用。我們應該知道,許多型別的物件也是可迭代的,比如 List、Set、Dictionary 和 String 等等。其他資料型別,像 range、map、filter,以及 pandas 包中的 Series、DataFrame,都是可迭代的。下面的程式碼演示了某些物件的使用方法。

>>> # 使用 range 物件
>>> integer_range = range(5)
>>> [x*x for x in integer_range]
[0,1,9,16]
>>> # 使用 Series 物件 
>>> import pandas as pd
>>> pd_series = pd.Series(range(5))
>>> print(pd_series)
0 0
1 1
2 2
3 3
4 4
dtype: int64
>>> [x*x for x in pd_series]
[0,16]

2.如果只需用到其中的某些元素,應當使用條件判斷語句

假設你需要將符合某種條件的元素歸集起來,並建立一個 list。下面展示了相關的語法。

[expression for item in iterable if condition]

if 語句用來實現條件判斷。下面的程式碼展示了這種用法的一個簡單示例。

>>> # 同樣建立一個 Integer 列表
>>> integers = [1,6]
>>> # 篩選出偶數,建立一個這些偶數的平方數列表
>>> squares_of_evens = [x*x for x in integers if x % 2 == 0]
>>> print((squares_of_evens))
[4,16,36]

3.使用條件判斷語句

List 物件中還可以使用 if-else 形式的條件判斷,語法如下。

[expression0 if condition else expression1 for item in iterable]

這跟前面的那種用法有些類似,別把這兩種用法混淆。在本例中,條件語句本身是一個整體。下面的程式碼提供了一個例子。

>>> # 建立一個 Integer 列表
>>> integers = [1,6]
>>> # 遍歷 integers 中的元素,如果是偶數,取平方數存入新的列表
>>> # 如果是奇數,取立方數存入新的列表
>>> custom_powers = [x*x if x % 2 == 0 else pow(x,3) for x in integers]
>>> print(custom_powers)
[1,27,125,36]

4.如果有巢狀結構,可以使用巢狀的迴圈

有可能可迭代物件中的元素自身也是可迭代的,儘管這種情況不太常見。如果你對巢狀的可迭代物件有興趣,可以使用 for 來實現迴圈巢狀。語法如下。

[expression for item_outer in iterable for item_inner in item_outer]

# 與下面的程式碼等同
for item_outer in iterable:
 for item_inner in item_outer:
  expression

上面的程式碼展示了使用for實現巢狀迴圈的例子。

>>> # 建立一個包含元組的列表
>>> prices = [('$5.99','$4.99'),('$3.5','$4.5')]
>>> # 獲取元組中的每個價格,以此建立一個一維列表
>>> prices_formatted = [float(x[1:]) for price_group in prices for x in price_group]
>>> print(prices_formatted)
[5.99,4.99,3.5,4.5]

5.替換高階函式

有的人比較習慣函數語言程式設計,比如使用高階函式也是這種習慣的表現之一。特別說明一下,高階函式是那些需要使用輸入或輸出引數的函式。在 Python 中,常用的高階函式有 map() 和 filter()。

>>> # 建立一個 integer 型別的列表
>>> integers = [1,5]
>>> # 使用 map 建立平方數列表
>>> squares_mapped = list(map(lambda x: x*x,integers))
>>> squares_mapped
[1,25]
>>> # 使用列表推導式建立平方數列表
>>> squares_listcomp = [x*x for x in integers]
>>> squares_listcomp
[1,25]
>>> # 使用 filter 取得 integers 中的偶數列表
>>> filtered_filter = list(filter(lambda x: x % 2 == 0,integers))
>>> filtered_filter
[2,4]
>>> # 使用列表推導式取得 integers 中的偶數列表
>>> filterd_listcomp = [x for x in integers if x % 2 == 0]
>>> filterd_listcomp
[2,4]

從上面的例子可以看出,使用 list 的某些特性比使用高階函式更具有可讀性,而且也能實現較複雜的巢狀結構。

使用禁忌

1.不要忘了定義建構函式

有人認為列表推導式很酷炫,是 Python 特有的功能,所以為了炫耀自己的 Python 水平,即使有更好替代方案也要使用它。

>>> # 使用 range 建立列表物件
>>> numbers = [x for x in range(5)]
>>> print(numbers)
[0,4]
>>> # 以一個字串為基礎,建立一個小寫字母的字元列表
>>> letters = [x.lower() for x in 'Smith']
>>> print(letters)
['s','m','i','t','h']

上述例子中,我們使用了 range 和 string,這兩種資料結構都是可迭代的,list()建構函式可以直接使用 iterable 建立一個 list 物件。下面的程式碼提供了更合理的解決方案。

>>> # 使用 range 建立列表物件
>>> numbers = list(range(5))
>>> print(numbers)
[0,4]
>>> # 以一個字串為基礎,建立一個小寫字母的字元列表
>>> letters = list('Smith'.lower())
>>> print(letters)
['s','h']

2.不要忘了生成器表示式

在 Python 中,生成器是一種特殊的可迭代物件,它會延遲載入元素,直到被請求才會載入。這在處理大量資料時會非常高效,它能提升儲存效率。相比之下,list 物件為了方便計數和索引,一次性建立所有的元素。所以跟生成器相比,在元素個數相同時,list 需要佔用更多記憶體。

我們可以定義一個生成器函式來建立生成器。我們也可以使用下面的語句來建立生成器,這是一種稱為生成器表示式的方法。

(expression for item in iterable)

你可能會注意到,除了使用圓括號外,它的語法跟使用 list 的語句很相似。所以需要注意區分。

考慮下面這個例子。我們要計算前一百萬個數字的平方和。如果使用 list 來實現,方法如下。

>>> # 建立列表物件 squares 
>>> squares = [x*x for x in range(10_000_000)]
>>> # 計算它們的總和
>>> sum(squares)
333333283333335000000
>>> squares.__sizeof__()
81528032

跟使用 list 相比,使用 generator 記憶體開銷小得多,只有 96 位元組。原因很簡單———— generator 不需要獲取所有的元素。相反,它只需要獲取各個元素在序列中的位置,建立下一個元素並呈現它,而且不必儲存在記憶體中。

結論

本文中,我們整理了 list 應用的一些關鍵要領。這些該做的和不該做的都非常清晰明瞭。我估計你會在合適的場景中用到它。下面是本文內容的小結。

  • 使用迭代的方式。 Python 中有許多型別的 iterable,你應當在掌握基礎(list 和 tuple)的同時融會貫通。
  • 使用條件判斷語句。 如果你對在 iterable 中篩選某些元素感興趣,可以多多研究條件判斷。
  • 使用條件判斷表示式。 如果你需要有選擇性地獲取某些資料,可以使用條件判斷表示式。
  • 使用巢狀的迴圈。 如果你要處理巢狀的 iterable,可以使用巢狀的迴圈結構。
  • 用 list 替代高階函式 在很多情況下,可以用 list 替代高階函式。
  • 不要忘記 list 的建構函式 定義 list 的建構函式,可以使用 iterable 建立一個 list 物件。如果你直接使用 iterable,推薦用這個方法。
  • 不要忘了生成器表示式 它的語法與 list 中的語法相似。在處理大量的物件時,這是一種節省記憶體開銷的辦法。list 和 generator 不同的是,為了日後的索引和訪問, list 必須提前建立,如果元素個數很多,就會消耗很大的記憶體。

以上就是Python 列表推導式需要注意的地方的詳細內容,更多關於Python 列表推導式的資料請關注我們其它相關文章!