Python 的 zip() 函數創建了一個迭代器,它將聚合來自兩個或多個可迭代對象的元素。您可以使用生成的迭代器快速一致地解決常見的編程問題,例如創建字典。在本教程中,您將發現 Python zip() 函數背後的邏輯以及如何使用它來解決實際問題。
返回元組的迭代器,其中第 i 個元組包含來自每個參數序列或可迭代對象的第 i 個元素。當最短的輸入迭代用完時,迭代器停止。使用單個可迭代參數,它返回一個 1 元組的迭代器。沒有參數,它返回一個空的迭代器。
您將在本教程的其余部分中解壓縮此定義。在處理代碼示例時,您會看到 Python 拉鏈操作就像包或牛仔褲上的物理拉鏈一樣工作。將拉鏈兩側的互鎖齒拉在一起以閉合開口。事實上,這個視覺類比非常適合理解 zip(),因為該函數是以物理拉鏈命名的!
Python 的 zip() 函數定義為 zip(*iterables)。該函數將可迭代對象作為參數並返回一個迭代器。此迭代器生成一系列元組,其中包含來自每個可迭代對象的元素。 zip() 可以接受任何類型的可迭代對象,例如文件、列表、元組、字典、集合等。
>>> numbers = [1, 2, 3]
>>> letters = ['a', 'b', 'c']
>>> zipped = zip(numbers, letters)
>>> zipped # Holds an iterator object
<zip object at 0x7fa4831153c8>
>>> type(zipped)
<class 'zip'>
>>> list(zipped)
[(1, 'a'), (2, 'b'), (3, 'c')]
在這裡,您使用 zip(numbers, letters) 創建一個生成 (x, y) 形式的元組的迭代器。在這種情況下,x 值取自數字,y 值取自字母。請注意 Python zip() 函數如何返回迭代器。要檢索最終的列表對象,您需要使用 list() 來使用迭代器。
如果您正在使用諸如列表、元組或字符串之類的序列,則可以保證從左到右評估您的可迭代對象。這意味著生成的元組列表將采用以下形式 [(numbers[0], letters[0]), (numbers[1], letters[1]),…, (numbers[n], letters[n ])]。但是,對於其他類型的可迭代對象(如集合),您可能會看到一些奇怪的結果:
>>> s1 = {
2, 3, 1}
>>> s2 = {
'b', 'a', 'c'}
>>> list(zip(s1, s2))
[(1, 'a'), (2, 'c'), (3, 'b')]
在這個例子中, s1 和 s2 是集合對象,它們的元素不保持任何特定的順序。這意味著 zip() 返回的元組將包含隨機配對的元素。如果您打算將 Python zip() 函數與無序的可迭代對象(如集合)一起使用,那麼請記住這一點。
您也可以不帶參數調用 zip()。在這種情況下,您只會得到一個空的迭代器:
>>> zipped = zip()
>>> zipped
<zip object at 0x7f196294a488>
>>> list(zipped)
[]
在這裡,您調用不帶參數的 zip(),因此您的 zipped 變量包含一個空迭代器。如果您使用 list() 使用迭代器,那麼您也會看到一個空列表。
您也可以嘗試強制空迭代器直接產生一個元素。在這種情況下,你會得到一個 StopIteration 異常:
>>> zipped = zip()
>>> next(zipped)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
當您在 zipped 上調用 next() 時,Python 會嘗試檢索下一項。然而,由於 zipped 包含一個空的迭代器,所以沒有什麼可以拉出,所以 Python 引發了一個 StopIteration 異常。
Python 的 zip() 函數也可以只接受一個參數。結果將是一個生成一系列單項元組的迭代器:
>>> a = [1, 2, 3]
>>> zipped = zip(a)
>>> list(zipped)
[(1,), (2,), (3,)]
這可能不是那麼有用,但它仍然有效。也許您可以找到一些 zip() 行為的用例!
如您所見,您可以根據需要使用盡可能多的輸入迭代來調用 Python zip() 函數。結果元組的長度將始終等於您作為參數傳遞的可迭代對象的數量。這是一個包含三個可迭代對象的示例:
>>> integers = [1, 2, 3]
>>> letters = ['a', 'b', 'c']
>>> floats = [4.0, 5.0, 6.0]
>>> zipped = zip(integers, letters, floats) # Three input iterables
>>> list(zipped)
[(1, 'a', 4.0), (2, 'b', 5.0), (3, 'c', 6.0)]
在這裡,您使用三個可迭代對象調用 Python zip() 函數,因此生成的元組每個都包含三個元素。
當您使用 Python zip() 函數時,請務必注意迭代的長度。您作為參數傳入的可迭代對象的長度可能不同。
在這些情況下, zip() 輸出的元素數量將等於最短迭代的長度。 zip() 將完全忽略任何不再可迭代的元素中的剩余元素,如您在此處看到的:
>>> list(zip(range(5), range(100)))
[(0, 0), (1, 1), (2, 2), (3, 3), (4, 4)]
因為 5 是第一個(也是最短的)range() 對象的長度,所以 zip() 輸出一個包含五個元組的列表。第二個 range() 對象中仍有 95 個不匹配的元素。這些都被 zip() 忽略,因為第一個 range() 對象中沒有更多元素來完成對。
循環多個迭代是 Python 的 zip() 函數最常見的用例之一。如果您需要遍歷多個列表、元組或任何其他序列,那麼您很可能會使用 zip()。本節將向您展示如何使用 zip() 同時遍歷多個可迭代對象。
Python 的 zip() 函數允許您並行迭代兩個或多個可迭代對象。由於 zip() 生成元組,您可以在 for 循環的標題中解壓縮它們:
>>> letters = ['a', 'b', 'c']
>>> numbers = [0, 1, 2]
>>> for l, n in zip(letters, numbers):
... print(f'Letter: {
l}')
... print(f'Number: {
n}')
...
Letter: a
Number: 0
Letter: b
Number: 1
Letter: c
Number: 2
在這裡,您遍歷 zip() 返回的一系列元組並將元素解壓縮為 l 和 n。當您結合 zip()、for 循環和元組解包時,您可以獲得一個有用的 Python 風格的習慣用法,用於一次遍歷兩個或多個可迭代對象。
您還可以在一個 for 循環中遍歷兩個以上的可迭代對象。考慮以下示例,它具有三個輸入迭代:
>>> letters = ['a', 'b', 'c']
>>> numbers = [0, 1, 2]
>>> operators = ['*', '/', '+']
>>> for l, n, o in zip(letters, numbers, operators):
... print(f'Letter: {
l}')
... print(f'Number: {
n}')
... print(f'Operator: {
o}')
...
Letter: a
Number: 0
Operator: *
Letter: b
Number: 1
Operator: /
Letter: c
Number: 2
Operator: +
在 Python 3.6 及更高版本中,字典是有序集合,這意味著它們將元素保持與引入時相同的順序。如果您利用此功能,則可以使用 Python zip() 函數以安全且連貫的方式遍歷多個字典:
>>> dict_one = {
'name': 'John', 'last_name': 'Doe', 'job': 'Python Consultant'}
>>> dict_two = {
'name': 'Jane', 'last_name': 'Doe', 'job': 'Community Manager'}
>>> for (k1, v1), (k2, v2) in zip(dict_one.items(), dict_two.items()):
... print(k1, '->', v1)
... print(k2, '->', v2)
...
name -> John
name -> Jane
last_name -> Doe
last_name -> Doe
job -> Python Consultant
job -> Community Manager
有一個問題經常出現在新 Pythonistas 的論壇中:“如果有一個 zip() 函數,那麼為什麼沒有 unzip() 函數做相反的事情?”
Python 中之所以沒有 unzip() 函數,是因為 zip() 的反義詞是……嗯,就是 zip()。你還記得 Python zip() 函數就像真正的拉鏈一樣工作嗎?到目前為止的示例已經向您展示了 Python 是如何關閉的。那麼,如何解壓縮 Python 對象呢?
假設您有一個元組列表,並且希望將每個元組的元素分成獨立的序列。為此,您可以將 zip() 與解包運算符 * 一起使用,如下所示:
>>> pairs = [(1, 'a'), (2, 'b'), (3, 'c'), (4, 'd')]
>>> numbers, letters = zip(*pairs)
>>> numbers
(1, 2, 3, 4)
>>> letters
('a', 'b', 'c', 'd')
在這裡,您有一個包含某種混合數據的元組列表。然後,您使用解包運算符 * 解壓縮數據,創建兩個不同的列表(數字和字母)。
排序是編程中的常見操作。假設您想合並兩個列表並同時對它們進行排序。為此,您可以將 zip() 與 .sort() 一起使用,如下所示:
>>> letters = ['b', 'a', 'd', 'c']
>>> numbers = [2, 4, 3, 1]
>>> data1 = list(zip(letters, numbers))
>>> data1
[('b', 2), ('a', 4), ('d', 3), ('c', 1)]
>>> data1.sort() # Sort by letters
>>> data1
[('a', 4), ('b', 2), ('c', 1), ('d', 3)]
>>> data2 = list(zip(numbers, letters))
>>> data2
[(2, 'b'), (4, 'a'), (3, 'd'), (1, 'c')]
>>> data2.sort() # Sort by numbers
>>> data2
[(1, 'c'), (2, 'b'), (3, 'd'), (4, 'a')]
在此示例中,您首先使用 zip() 組合兩個列表並對其進行排序。請注意 data1 如何按字母排序,而 data2 如何按數字排序。
您還可以同時使用 sorted() 和 zip() 來實現類似的結果:
>>> letters = ['b', 'a', 'd', 'c']
>>> numbers = [2, 4, 3, 1]
>>> data = sorted(zip(letters, numbers)) # Sort by letters
>>> data
[('a', 4), ('b', 2), ('c', 1), ('d', 3)]
在這種情況下, sorted() 會運行 zip() 生成的迭代器,並按字母對項目進行排序,這一切都在一次完成。這種方法可能會快一點,因為您只需要兩個函數調用:zip() 和 sorted()。
使用 sorted(),您還可以編寫更通用的代碼。這將允許您對任何類型的序列進行排序,而不僅僅是列表。
您可以使用 Python zip() 函數進行一些快速計算。假設您在電子表格中有以下數據:
您將使用這些數據來計算您的每月利潤。 zip() 可以為您提供一種快速的計算方法:
>>> total_sales = [52000.00, 51000.00, 48000.00]
>>> prod_cost = [46800.00, 45900.00, 43200.00]
>>> for sales, costs in zip(total_sales, prod_cost):
... profit = sales - costs
... print(f'Total profit: {
profit}')
...
Total profit: 5200.0
Total profit: 5100.0
Total profit: 4800.0
Python 的字典是一種非常有用的數據結構。有時,您可能需要從兩個不同但密切相關的序列構建字典。實現此目的的一種便捷方法是將 dict() 和 zip() 一起使用。例如,假設您從表單或數據庫中檢索了一個人的數據。現在您有以下數據列表:
>>> fields = ['name', 'last_name', 'age', 'job']
>>> values = ['John', 'Doe', '45', 'Python Developer']
使用此數據,您需要創建一個字典以進行進一步處理。在這種情況下,您可以將 dict() 與 zip() 一起使用,如下所示:
>>> a_dict = dict(zip(fields, values))
>>> a_dict
{
'name': 'John', 'last_name': 'Doe', 'age': '45', 'job': 'Python Developer'}
參考文章:https://realpython.com/python-zip-function/