清單的平行迭代
有的時候我們會擁有兩個或以上 相對應 的清單,在迭代的時候我們會希望他們能夠平行迭代。
平行迭代相同長度的清單
舉個例子,我們分別用三個清單來儲存班上學生的姓名、身高跟體重:
names = ['John', 'Mary', 'dokelung'] # 一樣假設只有三個學生
heights = [169, 158, 173]
weights = [62, 45, 72]
這三個清單互有關聯,且因為他們的順序一致,所以他們互相對應。由這三個清單我們可以得出 John 的身高是 169 公分,而他的體重是 62 公斤,而 Mary 158 公分,重 45 公斤,dokelung 則是 173 公分 72 公斤。
假設我們想要印出每個人的身高和體重,我們可能會出現以下寫法:
for i in range(len(names)):
print(names[i], heights[i], weights[i])
以下是結果:
John 169 62
Mary 158 45
dokelung 173 72
這個做法違背了我們一直強調的,盡量不要在迴圈中取值。根據前面的經驗,可能會有人想要試試看 enumerate
:
for i, name in enumerate(names):
print(name, heights[i], weights[i])
其實這樣做並沒有比較好,我們還是無法避免迴圈中取值,這樣反而破壞了取值方法的一致性。
真正好的方式應該是使用內建函式 zip
:
for name, h, w in zip(names, heights, weights):
print(name, h, w)
zip
會將多個可迭代序列的元素依照順序組合起來,成為一個元組,以本例而言,原先的三個清單將會被 zip
成三個 tuple:
('John', 169, 62)
('Mary', 158, 45)
('dokelung', 173, 72)
於是我們在迭代時就可以簡單地運用拆解的技巧獲得來自三個清單的對應元素。
平行迭代不同長度的清單
不過有的時候清單的長度並不相同,比如下例:
names = ['John', 'Mary', 'dokelung', 'Steven'] # 現在有四個學生了
heights = [169, 158, 173] # 但卻只有前三個學生有對應的身高
如果我們並不關心多餘的資料的話 (以這個例子來說,就是如果我們不關心 Steven 的身高),一樣用 zip
就可以解決問題,因為 zip
的設計就是以較短資料為主:
for name, h in zip(names, heights):
print(name, h)
但相反的,如果今天我們想要印出所有出現在 names
裡的名字,這時就要以較長的清單為主了,使用 zip_longest
是個好主意:
from itertools import zip_longest # zip_longest 並非內建函式,我們得從 itertools 匯入他
for name, h in zip_longest(names, heights, fillvalue='UNKNOWN'):
print(name, h)
zip_longest
有個參數叫做 fillvalue
,用來設定當較短序列無元素可迭代時,使用的填充值,在這裡我們使用 UNKNOWN
。
程式的輸出:
John 169
Mary 158
dokelung 173
Steven UNKNOWN
我們會在之後的章節中繼續討論
itertools
模組