使用 Python 列表推導式表示法

商業

在 Python 中,生成新列表時使用列表推導表示法很簡單。(List comprehensions)

在本文中,我們將首先討論以下內容

  • 列表理解符號的基本類型
  • 用 if 條件分支列出理解符號
  • 與三元運算符的組合(if else-like處理)
  • zip(),enumerate()與這些結合
  • 嵌套列表包含符號

接下來,我們將通過示例代碼解釋列表理解符號集。

  • 設置包含符號(Set comprehensions)
  • 字典包含符號(Dict comprehensions)
  • 發電機類型(Generator expressions)

列表理解符號的基本類型

列表理解符號寫成如下。

[Expression for Any Variable Name in Iterable Object]

它通過任意變量名稱獲取可迭代對象(例如列表、元組或範圍)的每個元素,並使用表達式對其進行評估。返回以評估結果為元素的新列表。

給出了一個示例以及等效的 for 語句。

squares = [i**2 for i in range(5)]
print(squares)
# [0, 1, 4, 9, 16]
squares = []
for i in range(5):
    squares.append(i**2)

print(squares)
# [0, 1, 4, 9, 16]

可以使用 map() 完成相同的過程,但首選列表理解表示法,因為它簡單明了。

用 if 條件分支列出理解符號

也可以使用 if 進行條件分支。在後綴中寫 if 如下。

[Expression for Any Variable Name in Iterable Object if Conditional Expression]

表達式只對條件表達式為真的可迭代對象的元素進行評估,並返回一個元素為結果的新列表。

您可以在條件表達式中使用任何變量名稱。

給出了一個示例以及等效的 for 語句。

odds = [i for i in range(10) if i % 2 == 1]
print(odds)
# [1, 3, 5, 7, 9]
odds = []
for i in range(10):
    if i % 2 == 1:
        odds.append(i)

print(odds)
# [1, 3, 5, 7, 9]

可以使用 filter() 完成相同的過程,但首選列表理解表示法,因為它簡單明了。

與三元運算符的組合(if else-like處理)

在上面的例子中,只處理那些符合條件的元素,不符合條件的元素被排除在新列表之外。

如果要根據條件切換流程,或者要以不同方式處理不滿足條件的元素(如 if else),請使用三元運算符。

在 Python 中,三元運算符可以寫成如下

Value When True if Conditional Expression else Value When False

這用於列表理解符號的表達式部分,如下所示。

[Value When True if Conditional Expression else Value When False for Any Variable Name in Iterable Object]

給出了一個示例以及等效的 for 語句。

odd_even = ['odd' if i % 2 == 1 else 'even' for i in range(10)]
print(odd_even)
# ['even', 'odd', 'even', 'odd', 'even', 'odd', 'even', 'odd', 'even', 'odd']
odd_even = []
for i in range(10):
    if i % 2 == 1:
        odd_even.append('odd')
    else:
        odd_even.append('even')

print(odd_even)
# ['even', 'odd', 'even', 'odd', 'even', 'odd', 'even', 'odd', 'even', 'odd']

也可以使用任意變量名稱為真值和假值編寫表達式。

如果滿足條件,則進行一些處理,否則原始可迭代對象的值保持不變。

odd10 = [i * 10 if i % 2 == 1 else i for i in range(10)]
print(odd10)
# [0, 10, 2, 30, 4, 50, 6, 70, 8, 90]

結合 zip() 和 enumerate()

for 語句中經常使用的有用函數包括 zip(),它組合了多個可迭代對象,以及 enumerate(),它返回一個值及其索引。

當然,可以將 zip() 和 enumerate() 與列表理解符號一起使用。它不是一種特殊的語法,如果考慮與 for 語句的對應關係,也不難。

zip() 示例。

l_str1 = ['a', 'b', 'c']
l_str2 = ['x', 'y', 'z']

l_zip = [(s1, s2) for s1, s2 in zip(l_str1, l_str2)]
print(l_zip)
# [('a', 'x'), ('b', 'y'), ('c', 'z')]
l_zip = []
for s1, s2 in zip(l_str1, l_str2):
    l_zip.append((s1, s2))

print(l_zip)
# [('a', 'x'), ('b', 'y'), ('c', 'z')]

enumerate() 的例子。

l_enu = [(i, s) for i, s in enumerate(l_str1)]
print(l_enu)
# [(0, 'a'), (1, 'b'), (2, 'c')]
l_enu = []
for i, s in enumerate(l_str1):
    l_enu.append((i, s))

print(l_enu)
# [(0, 'a'), (1, 'b'), (2, 'c')]

使用 if 時的想法與之前相同。

l_zip_if = [(s1, s2) for s1, s2 in zip(l_str1, l_str2) if s1 != 'b']
print(l_zip_if)
# [('a', 'x'), ('c', 'z')]

每個元素也可以用來計算一個新元素。

l_int1 = [1, 2, 3]
l_int2 = [10, 20, 30]

l_sub = [i2 - i1 for i1, i2 in zip(l_int1, l_int2)]
print(l_sub)
# [9, 18, 27]

嵌套列表包含符號

與嵌套 for 循環一樣,列表理解符號也可以嵌套。

[Expression for Variable Name 1 in Iterable Object 1
    for Variable Name 2 in Iterable Object 2
        for Variable Name 3 in Iterable Object 3 ... ]

為方便起見,添加了換行和縮進,但不是語法所必需的;它們可以在一行上繼續。

給出了一個示例以及等效的 for 語句。

matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]

flat = [x for row in matrix for x in row]
print(flat)
# [1, 2, 3, 4, 5, 6, 7, 8, 9]
flat = []
for row in matrix:
    for x in row:
        flat.append(x)

print(flat)
# [1, 2, 3, 4, 5, 6, 7, 8, 9]

也可以使用多個變量。

cells = [(row, col) for row in range(3) for col in range(2)]
print(cells)
# [(0, 0), (0, 1), (1, 0), (1, 1), (2, 0), (2, 1)]

您還可以進行條件分支。

cells = [(row, col) for row in range(3)
         for col in range(2) if col == row]
print(cells)
# [(0, 0), (1, 1)]

也可以為每個可迭代對像有條件地分支。

cells = [(row, col) for row in range(3) if row % 2 == 0
         for col in range(2) if col % 2 == 0]
print(cells)
# [(0, 0), (2, 0)]

設置包含符號(Set comprehensions)

將列表推導式表示法中的方括號 [] 更改為大括號 {} 創建一個集合(集合類型對象)。

{Expression for Any Variable Name in Iterable Object}
s = {i**2 for i in range(5)}

print(s)
# {0, 1, 4, 9, 16}

字典包含符號(Dict comprehensions)

字典(字典類型對象)也可以用理解符號生成。

{},並將表達式部分中的鍵和值指定為 key: value。

{Key: Value for Any Variable Name in Iterable Object}

可以為鍵和值指定任何表達式。

l = ['Alice', 'Bob', 'Charlie']

d = {s: len(s) for s in l}
print(d)
# {'Alice': 5, 'Bob': 3, 'Charlie': 7}

要從鍵和值列表創建新字典,請使用 zip() 函數。

keys = ['k1', 'k2', 'k3']
values = [1, 2, 3]

d = {k: v for k, v in zip(keys, values)}
print(d)
# {'k1': 1, 'k2': 2, 'k3': 3}

發電機類型(Generator expressions)

如果列表推導式表示法中的方括號 [] 用作圓括號 (),則返回生成器而不是元組。這稱為生成器表達式。

列表理解符號的示例。

l = [i**2 for i in range(5)]

print(l)
# [0, 1, 4, 9, 16]

print(type(l))
# <class 'list'>

生成器表達式的示例。如果按原樣打印() 生成器,它不會打印出其內容,但如果使用 for 語句運行它,則可以獲得內容。

g = (i**2 for i in range(5))

print(g)
# <generator object <genexpr> at 0x10af944f8>

print(type(g))
# <class 'generator'>

for i in g:
    print(i)
# 0
# 1
# 4
# 9
# 16

生成器表達式還允許使用 if 和列表理解符號進行條件分支和嵌套。

g_cells = ((row, col) for row in range(0, 3)
           for col in range(0, 2) if col == row)

print(type(g_cells))
# <class 'generator'>

for i in g_cells:
    print(i)
# (0, 0)
# (1, 1)

例如,如果使用列表理解表示法生成具有大量元素的列表,然後使用 for 語句循環,則如果使用列表理解表示法,將在開頭生成包含所有元素的列表。另一方面,如果使用生成器表達式,每次重複循環時,元素都會一個一個地生成,從而減少使用的內存量。

如果生成器表達式是函數的唯一參數,則可以省略圓括號 ()。

print(sum([i**2 for i in range(5)]))
# 30

print(sum((i**2 for i in range(5))))
# 30

print(sum(i**2 for i in range(5)))
# 30

至於處理速度,當處理所有元素時,列表理解符號通常比生成器符號快。

但是,例如,在使用 all() 或 any() 進行判斷時,會在存在 false 或 true 時確定結果,因此使用生成器表達式可能比使用列表推導式表示法更快。

沒有元組推導表示法,但是如果您使用生成器表達式作為 tuple() 的參數,則可以使用推導表示法生成一個元組。

t = tuple(i**2 for i in range(5))

print(t)
# (0, 1, 4, 9, 16)

print(type(t))
# <class 'tuple'>