for Comprehension
April 14, 2022如果使用者輸入的命令列引數是數字,想將這些數字全部進行平方運算,該怎麼做呢?現在的你,也許會想出這樣的寫法:
import sys
squares = []
for arg in sys.argv[1:]:
squares.append(int(arg) ** 2)
print(squares)
map 映射
將一個 list
轉為另一個 list
,是很常見的操作,Python 針對這類需求,提供了for
Comprehension 語法,你可以如下實現需求:
import sys
squares = [int(arg) ** 2 for arg in sys.argv[1:]]
print(squares)
對於 for arg in sys.argv[1:]
這部份,其作用是逐一迭代出命令列引數指定給 arg
變數,之後執行 for
左方的 int(arg) ** 2
運算,使用[]含括起來,表示每次迭代的運算結果,會被收集為一個 list
。一個執行結果如下:
>python square.py 10 20 30
[100, 400, 900]
filter 過濾
for
Comprehension 也可以與條件式結合,這可以構成一個過濾的功能。例如想收集某個 list
中的奇數元素至另一 list
,在不使用 for
Comprehension下,可以如下撰寫:
import sys
odds = []
for arg in sys.argv[1:]:
if int(arg) % 2:
odds.append(arg)
print(odds)
若使用 for
Comprehension 的話,可以改寫為以下的程式碼:
import sys
odds = [arg for arg in sys.argv[1:] if int(arg) % 2]
print(odds)
在這個例子中,只有在 if
條件式成立時,for
左邊的運算式才會被執行,並收集為最後結果 list
中的元素。一個執行結果如下:
>python odds.py 11 8 9 5 4 6 3 2
['11', '9', '5', '3']
for
Comprehension 如果要形成巢狀結構也是可行的,不過建議別太過火,不然可讀性會迅速降低。簡單地將矩陣表示為一維的 list
倒還不錯:
>>> matrix = [
... [1, 2, 3],
... [4, 5, 6],
... [7, 8, 9]
... ]
>>> array = [element for row in matrix for element in row]
>>> array
[1, 2, 3, 4, 5, 6, 7, 8, 9]
>>>
另一個例子是,使用 for
Comprehension 來取得兩個序列的排列組合:
>>> [letter1 + letter2 for letter1 in 'Justin' for letter2 in 'momor']
['Jm', 'Jo', 'Jm', 'Jo', 'Jr', 'um', 'uo', 'um', 'uo', 'ur', 'sm', 'so', 'sm', 'so', 'sr', 'tm', 'to', 'tm', 'to', 'tr', 'im', 'io', 'im', 'io', 'ir', 'nm', 'no', 'nm', 'no', 'nr']
>>>
惰性求值
在 for
Comprehension 兩旁放上 []
,表示會產生 list
,如果資料來源很長,或者資料來源本身,是個有惰性求值特性的產生器時,直接產生 list
顯得沒有效率,這時可以在 for
Comprehension兩旁放上 ()
,這樣的話就會建立一個 generator
物件,具有惰性求值(lazy evaluation)特性。
舉個例子來說,Python 中有個 sum
函式,可以計算指定序列的數字加總值,像是若傳遞 sum([1, 2, 3])
的話,結果會是 6。如果想計算 1 到 10000 的加總值呢?使用 sum([n for n in range(1, 10001)])
是可以達到目的,不過,這會先產生具有 10000 個元素的 list
,然後再交給 sum
函式運算,此時可以寫成 sum(n for n in range(1, 10001))
,就不會有產生 list
的負擔。
這邊其實也在說明,只要寫 n for n in range(1, 10001)
就是個產生器運算式了,因此在傳給 sum
函式時,不必再寫成 sum((n for n in range(1, 10001)))
,需要加上括號的情況,是在需要直接參考一個產生器的時候,例如 g = (n for n in range(1, 10001))
的情況。
應用於 set/dict/tuple
for
Comprehension 也可用來建立 set
,只要在 for
Comprehension兩旁放上 {}
。例如,建立一個 set
,其中包括了來源字串中不重複的大寫字母。
>>> text = 'Your Right brain has nothing Left. Your Left brain has nothing Right'
>>> {c for c in text if c.isupper()}
{'Y', 'R', 'L'}
>>>
若是想使用 for
Comprehension 來建立 dict
實例,也是可行的。例如:
>>> names = ['Justin', 'Monica', 'Irene']
>>> passwds = [123456, 654321, 13579]
>>> {name : passwd for name, passwd in zip(names, passwds)}
{'Justin': 123456, 'Irene': 13579, 'Monica': 654321}
>>>
上面的 zip
,將 names
與 passwds
兩兩相扣在一起成為 tuple
,每個 tuple
中的一對元素,會在 for
Comprehension 中拆解指定給 name
與 passwd
,最後 name
與 passwd
組成 dict
的每一對鍵值。
那麼,可以使用 for
Comprehension建立 tuple
嗎?可以的,不過不是在 for
Comprehension兩旁放上 ()
,這樣的話就會建立一個 generator
物件,而不是 tuple
,想要用 for
Comprehension 建立 tuple
的話,可以將 for
Comprehension 產生器運算式傳給 tuple
。例如:
>>> tuple(n for n in range(10))
(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
>>>