zip 在英文是拉鍊的意思,而 Python 的內建函式 zip()
就是取名自拉鍊的形象,它可以同時迭代多個 list、每次分別從各個 list 中取一個元素配成同一組,彷彿拉鍊齒一組一組整齊對應的樣子。
這則筆記中,好豪將跟你分享 zip()
函式有哪些好用的地方,我會先簡短教學 zip()
函式的功能,並把重點放在能讓你快速活用的三項實戰案例:
矩陣轉置 :用 Python 解 LeetCode 演算法題目常用技巧dict 反轉 :NLP 資料分析建立詞庫同時迭代多個長短不一的 list :zip()
的進階用法Python zip()
每次從各個引數 list 中各取一個元素配成同一組,彷彿拉鍊齒一個個整齊對應的樣子(Source: Unsplash ) 快速學習 zip 函式zip()
:從每個引數中的可迭代物件,組合各個可迭代物件的元素,產生一個迭代器Python3 官方文件
在 Python3,zip()
會回傳一個迭代器(iterator),而這個迭代器的第 i 個元素,就是每個傳入引數的第 i 個內容所組合成的 tuple 。zip()
的引數需要是可迭代物件,包括常用的 list、set、dict、或者 str 等等。請看以下範例:
>>> type ( zipped ) # 回傳的是一個 'zip' 物件,它是可迭代的
< zip object at 0x108e8bc80 >
# 也可用 list() 或 set() 將迭代器轉換成其他資料型態
[( 'a' , 1 ) , ( 'b' , 2 ) , ( 'c' , 3 )]
{( 'c' , 3 ) , ( 'b' , 2 ) , ( 'a' , 1 )}
## 函式引數需要是可迭代物件,因此也包括 str, set, dict 等等
>>> z = { 'first' : 0.5 , 'second' : 0.6 , 'third' : 0.7 }
>>> for i in zip ( x, y, z ) :
## 1. 如果你 zip 遍歷內容是 set,要注意它沒有排序的特性
## 本例 set 的 print 的順序就跟宣告內容順序不同
## 2. 當你直接遍歷 dict 物件的時候,只會遍歷 key、而不是 value 喔
## 如果需要遍歷 dict 的 value,可以使用 .values() 函式
>>> for i in zip ( x, y, z. values ()) :
究竟使用 zip()
函式有什麼好處呢?我們直接從以下三個實戰案例來學習。
矩陣轉置給定一個 3*2 的矩陣(在 Python 內的資料結構可以用 3 個 list、各有 2 個元素來表示),要如何轉置成 2*3 的矩陣呢?
矩陣轉置範例(Source: 維基百科 ) 矩陣轉置只需要使用 zip(*matrix)
這個簡單技巧就能完成:
>>> for i in zip ( *matrix ) :
## 如果轉置後希望維持原本 list of lists 的結構
>>> [ list ( row ) for row in zip ( *matrix )]
原理說明:用 *
符號解包呼叫函式的時候,如果在可迭代物件的引數前面加上 *
符號,可以進行解包(unpack) :將可迭代物件內的每個內容物各自成為引數。
如果呼叫 func(my_list)
,函式只會收到一個引數 如果呼叫 func(*my_list)
,該 list 長度多長、函式就收到幾個引數 在轉置矩陣的範例中,矩陣解包後,每一列各自成為一個引數 傳入 zip()
了。
>>> for i in zip ( *matrix ) :
>>> for i in zip ([ 1 , 2 ] , [ 3 , 4 ] , [ 5 , 6 ]) :
LeetCode 實戰此範例來自 LeetCode 477 (難度 Medium) ,筆者在此只探討用到 zip()
解題的部分。
Hamming Distance 是要計算兩個二進位制的數總共有幾個位元(bit)不一樣,而本題是需要你計算多個 數的 Hamming Distance 總和。例如,要計算 1、7、與 10 三個數在二進位制總共有幾個位元不同。
如何一個個位元比較呢?用 zip()
處理二進位制字串 就很好解決了!
>>> arr = [ f "{i:04b}" for i in arr ]
zip()
幫你把各個數的同一個位元都整齊放在同一列了,接下來你只要專注想出每一列要怎麼加總位數差異就可以了!
如果這題解完你還覺得練習不夠,和你分享 LeetCode 這題 也適合用 zip()
解題。
dict 反轉處理資料的時候,zip()
可以用來將 dict 的 key 與 value 關係反轉。
>>> my_dict = { 'a' : 1 , 'b' : 2 , 'c' : 3 }
dict_keys ([ 'a' , 'b' , 'c' ])
dict_items ([( 'a' , 1 ) , ( 'b' , 2 ) , ( 'c' , 3 )])
>>> { value: key for key, value in my_dict. items ()}
>>> dict ( zip ( my_dict. values () , my_dict. keys ()))
上面提了兩種方式做到 dict 的 key 與 value 反轉,讀者可以依照自己的寫程式風格選擇要用哪種。
運用場景:NLP 建立詞庫在做 NLP 文字分析時,會需要建立詞庫 ,文字要編成索引編號(index)才能輸入到機器學習模型,又因為模型輸出也是索引編號,所以我們還需要將索引編號轉換回來原本的文字。
要同時做到 文字 -> index
以及 index -> 文字
,基本作法就是創造兩個 key 與 value 相反的 dict。
>>> sentence = "My name is haohao. This is my blog."
>>> sentence_list = [ w. lower () . strip ( '.' ) for w in sentence. split ()]
[ 'my' , 'name' , 'is' , 'haohao' , 'this' , 'is' , 'my' , 'blog' ]
>>> set ( sentence_list ) # 用 set 去除重複字詞
{ 'is' , 'name' , 'this' , 'haohao' , 'my' , 'blog' }
>>> word2index = { w: i for i, w in enumerate ( set ( sentence_list ))}
{ 'is' : 0 , 'name' : 1 , 'this' : 2 , 'haohao' : 3 , 'my' : 4 , 'blog' : 5 }
## 再用 zip() 反轉 dict,建立 `index -> 文字` 的對應關係
>>> index2word = dict ( zip ( word2index. values () , word2index. keys ()))
{ 0 : 'is' , 1 : 'name' , 2 : 'this' , 3 : 'haohao' , 4 : 'my' , 5 : 'blog' }
## 可以任意讓資料在 `文字` 與 `index` 之間自由切換囉!
[ 'my' , 'name' , 'is' , 'haohao' , 'this' , 'is' , 'my' , 'blog' ]
>>> sentence_index_list = [ word2index [ w ] for w in sentence_list ]
>>> [ index2word [ i ] for i in sentence_index_list ]
[ 'my' , 'name' , 'is' , 'haohao' , 'this' , 'is' , 'my' , 'blog' ]
NLP 詞庫建立,通常還需要考慮詞頻等問題,可以參考 Udacity 課程教學的簡單詞庫寫法(Github) 。如果對入門 NLP 與深度學習有更多興趣,推薦你閱讀筆者好豪 學習這門 Udacity 深度學習 Pytorch 課程的心得 ,一起來上課!
同時迭代多個長短不一的 list請注意:如果你傳給 zip()
的多個可迭代物件長度不一樣,zip()
不會回報 error,而是走訪完最短的可迭代物件就結束。
如果你沒察覺到傳入的多個 list 長短不一,長度較長的 list 數值就會在走訪時被遺漏 !
# 範例:多個 list 長短不一,又想用 zip() 同時走訪
>>> list_b = [ 'a' , 'b' , 'c' , 'd' , 'e' ]
>>> list_c = [ 0.5 , 0.7 , 0.9 ]
>>> for i in zip ( list_a, list_b, list_c ) :
## 使用 zip() 之後只剩下走訪 2 個元素!
## 如果 zip() 引數之中混入一個長度為 0 的 list
>>> for i in zip ([] , list_a, list_b, list_c ) :
>>> len ( list ( zip ([] , list_a, list_b, list_c )))
如果你需要以最長的 list 為準 、用 zip()
同時走訪多個 list,可以把 zip()
替換成 itertools
內建函式庫 裡的 zip_longest()
函式:
>>> from itertools import zip_longest
>>> for i in zip_longest ( list_a, list_b, list_c ) :
## 上例中,較短的 list 先被走訪完畢,之後都預設填補 None
## 設定 zip_longest() 的 fillvalue 引數可以選擇填補其他內容
>>> for i in zip_longest ( list_a, list_b, list_c, fillvalue= "NO_VALUE" ) :
( 'NO_VALUE' , 'd' , 'NO_VALUE' )
( 'NO_VALUE' , 'e' , 'NO_VALUE' )
## 要用 zip() 同時走訪長短不一的 list、並且要把最長的 list 走訪完
## - cycle(): 較短的 list 被走訪完後,會從頭再走訪、不斷重複
>>> from itertools import cycle, repeat
>>> for i in zip ( cycle ( list_a ) , list_b, cycle ( list_c ) , repeat ( "ʕ •ᴥ•ʔ" )) :
## 想更加了解 itertools 的讀者,可以參考官方文件
## https://docs.python.org/zh-cn/3/library/itertools.html
結語在這則筆記裡,我們學會了:
zip()
的基本用法矩陣轉置 dict 反轉 還有長短不一的 list 同時迭代進階技巧 zip()
函式不只方便好用,筆者好豪也常在網友分享的 面試題 看到與它相關的問題出現,可以說是邁向進階 Python 必學的內建函式,相信讀者掌握這則筆記的技巧後,不論是刷演算法題或資料分析工作,都能多增加一項寫 Python 利器!
參考資料:還想知道更多 Python 相關技巧嗎?推薦你閱讀好豪蒐集的《Python 神乎其技》免費教學文章 ,學會更多 Pythonic Code!
如果這篇文章有幫助到你,歡迎追蹤 好豪的粉絲專頁 ,我會持續分享 Python 技巧、資料科學等知識。
也歡迎點選下方按鈕將本文加入書籤隨時複習、或者分享給更多正在學 Python 的朋友。
from: https://haosquare.com/python-zip-function/