Mám dve iterable v jazyku Python a chcem ich prebrať po dvojiciach:
foo = (1, 2, 3)
bar = (4, 5, 6)
for (f, b) in some_iterator(foo, bar):
print "f: ", f, "; b: ", b
Výsledkom by malo byť:
f: 1; b: 4
f: 2; b: 5
f: 3; b: 6
Jedným spôsobom je iterácia cez indexy:
for i in xrange(len(foo)):
print "f: ", foo[i], "; b: ", b[i]
Ale to sa mi zdá trochu nepythonické. Existuje lepší spôsob, ako to urobiť?
for f, b in zip(foo, bar):
print(f, b)
zip
sa zastaví, keď sa zastaví kratšie z foo
alebo bar
.
V Pythone 3 je zip
vráti iterátor tuplov, podobne ako itertools.izip
v Pythone 2. Ak chcete získať zoznam
tuplov, použite list(zip(foo, bar))
. A na zipovanie, kým oba iterátory nie sú
vyčerpané, použijete príkaz
itertools.zip_longest.
V Pythone 2 sa používa zip
vráti zoznam kôpok. To je v poriadku, keď foo
a bar
nie sú masívne. Ak sú obe masívne, potom je vytvorenie zip(foo,bar)
zbytočne masívne
dočasná premenná a mala by sa nahradiť premennou itertools.izip
alebo
itertools.izip_longest
, ktoré namiesto zoznamu vrátia iterátor.
import itertools
for f,b in itertools.izip(foo,bar):
print(f,b)
for f,b in itertools.izip_longest(foo,bar):
print(f,b)
izip
sa zastaví, keď sa vyčerpá buď foo
alebo bar
.
izip_longest
sa zastaví, keď sa vyčerpá foo
aj bar
.
Keď sú kratšie iterátory vyčerpané, izip_longest
poskytne tuple s None
na pozícii zodpovedajúcej danému iterátoru. Ak chcete, môžete okrem hodnoty None
nastaviť aj inú hodnotu fillvalue
. Pozri tu celý príbeh.
Všimnite si tiež, že zip
a jeho príbuzný zip
môžu ako argumenty prijímať ľubovoľný počet iterabilít. Napríklad,
for num, cheese, color in zip([1,2,3], ['manchego', 'stilton', 'brie'],
['red', 'blue', 'green']):
print('{} {} {}'.format(num, color, cheese))
vypíše
1 red manchego
2 blue stilton
3 green brie