La funzione zip() è comparsa già in Python 2 (allora restituiva una lista), mentre in Python 3 restituisce un iteratore pigro. Essa "cucisce" insieme più sequenze in tuple elemento per elemento, rendendo la gestione di collezioni iterabili parallele comoda ed efficiente.
Spesso è necessario elaborare più elenchi (o altre sequenze) contemporaneamente - ad esempio, iterare su coppie chiave-valore o gestire le coordinate punto-coppia. La sincronizzazione manuale degli indici è fonte di errori e scarsa leggibilità del codice, specialmente per collezioni di lunghezza diversa.
La funzione zip() accetta qualsiasi numero di oggetti iterabili e restituisce un iteratore di tuple, ognuna delle quali contiene gli elementi corrispondenti di ciascun iterabile. Se le sequenze sono di lunghezza diversa, il risultato si interrompe sulla più corta.
names = ['Alice', 'Bob', 'Charlie'] ages = [24, 27, 30] for name, age in zip(names, ages): print(f'{name} ha {age} anni')
È possibile disimballare zip usando *:
pairs = [(1, 'a'), (2, 'b'), (3, 'c')] nums, chars = zip(*pairs) print(nums) # (1, 2, 3) print(chars) # ('a', 'b', 'c')
Cosa succede se si passa a zip() collezioni di lunghezza diversa?
zip() si fermerà quando raggiungerà la fine della collezione più corta - gli elementi rimanenti delle collezioni più lunghe verranno ignorati.
print(list(zip([1,2,3], ['a','b']))) # [(1, 'a'), (2, 'b')]
Come ottenere tuple riempiendo le sequenze più corte con un valore predefinito?
La zip() standard non lo fa, ma esiste itertools.zip_longest per questo comportamento:
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
È possibile "scomporre" il risultato di zip(), per ottenere nuovamente le liste originali?
Sì, se tutte le collezioni originali avevano la stessa lunghezza e il risultato non è stato modificato, l'operatore * consente di disimballare zip.
pairs = [(1,2), (3,4)] a, b = zip(*pairs) print(a) # (1, 3) print(b) # (2, 4)
Gestire collezioni correlate di lunghezza diversa, senza considerare le peculiarità di 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) e 'c', 'd' da lst1 non sono stati elaborati
Pro:
Contro:
Utilizzo di zip_longest con fillvalue, per non perdere nessun elemento:
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 ?
Pro:
Contro: