Funkcja zip() pojawiła się już w Pythonie 2 (wtedy zwracała listę), a w Pythonie 3 zwraca leniwy iterator. "Łączy" kilka sekwencji w krotki element po elemencie, co uczyniło przetwarzanie równoległych kolekcji wygodnym i efektywnym.
Często trzeba przetwarzać kilka list (lub innych rodzajów sekwencji) jednocześnie — na przykład przejść przez parę klucz-wartość lub przetworzyć pary współrzędne. Samodzielna synchronizacja indeksów jest źródłem błędów i nieczytelności kodu, szczególnie dla kolekcji o różnej długości.
Funkcja zip() przyjmuje dowolną liczbę obiektów iterowalnych i zwraca iterator krotek, z których każda zawiera odpowiednie elementy każdego z iterowalnych. Jeśli sekwencje mają różną długość, wynik jest obcinany do najkrótszej.
names = ['Alice', 'Bob', 'Charlie'] ages = [24, 27, 30] for name, age in zip(names, ages): print(f'{name} ma {age} lat')
Można rozwinąć zip za pomocą *:
pairs = [(1, 'a'), (2, 'b'), (3, 'c')] nums, chars = zip(*pairs) print(nums) # (1, 2, 3) print(chars) # ('a', 'b', 'c')
Co się stanie, jeśli przekażesz zip() kolekcje o różnej długości?
zip() zatrzyma się, gdy osiągnie koniec najkrótszej kolekcji — pozostałe elementy dłuższych kolekcji zostaną zignorowane.
print(list(zip([1,2,3], ['a','b']))) # [(1, 'a'), (2, 'b')]
Jak uzyskać krotki, uzupełniając krótsze sekwencje wartością domyślną?
Standardowa zip() tego nie obsługuje, ale istnieje itertools.zip_longest dla takiego zachowania:
from itertools import zip_longest for a, b in zip_longest([1,2], ['x','y','z'], fillvalue=None): print(a, b) # 1 x # 2 y # None z
Czy można "rozpakować" wynik zip(), aby ponownie uzyskać pierwotne listy?
Tak, jeśli wszystkie pierwotne kolekcje miały tę samą długość i wynik nie został zmieniony, operator * pozwala rozwinąć zip.
pairs = [(1,2), (3,4)] a, b = zip(*pairs) print(a) # (1, 3) print(b) # (2, 4)
Przetwarzanie powiązanych kolekcji o różnej długości, nie uwzględniając cech zip:
lst1 = [1,2,3,4] lst2 = ['a','b'] for x, y in zip(lst1, lst2): print(x, y) # 1 a # 2 b # (3,4) i 'c', 'd' z lst1 nie zostały przetworzone
Zalety:
Wady:
Użycie zip_longest z fillvalue, aby nie zgubić żadnego elementu:
from itertools import zip_longest lst1 = [1,2,3,4] lst2 = ['a','b'] for x, y in zip_longest(lst1, lst2, fillvalue='?'): print(x, y) # 1 a # 2 b # 3 ? # 4 ?
Zalety:
Wady: