Tengo dos iterables en Python, y quiero repasarlos de dos en dos:
foo = (1, 2, 3)
bar = (4, 5, 6)
for (f, b) in some_iterator(foo, bar):
print "f: ", f, "; b: ", b
Debería dar como resultado:
f: 1; b: 4
f: 2; b: 5
f: 3; b: 6
Una forma de hacerlo es iterar sobre los índices:
for i in xrange(len(foo)):
print "f: ", foo[i], "; b: ", b[i]
Pero eso me parece poco sintético. Hay una mejor manera de hacerlo?
for f, b in zip(foo, bar):
print(f, b)
zip
se detiene cuando el más corto de foo
o bar
se detiene.
En Python 3, zip
devuelve un iterador de tuplas, como itertools.izip
en Python2. Para obtener una lista
de tuplas, usa list(zip(foo, bar))
. Y para comprimir hasta que ambos iteradores se
se agoten, se usaría
itertools.zip_longest.
En Python 2, zip
devuelve una lista de tuplas. Esto está bien cuando foo
y bar
no son masivos. Si ambos son masivos, entonces formar zip(foo,bar)
es una variable temporal innecesariamente masiva
innecesariamente masiva, y debería ser reemplazada por itertools.izip
o
itertools.izip_longest
, que devuelve un iterador en lugar de una lista.
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
se detiene cuando se agota foo
o bar
.
izip_longest
se detiene cuando tanto foo
como bar
se agotan.
Cuando se agotan los iteradores más cortos, izip_longest
devuelve una tupla con None
en la posición correspondiente a ese iterador. También puede establecer un fillvalue
diferente además de None
si lo desea. Vea aquí la historia completa.
Tenga en cuenta también que zip
y su hermano zip
pueden aceptar un número arbitrario de iterables como argumentos. Por ejemplo,
for num, cheese, color in zip([1,2,3], ['manchego', 'stilton', 'brie'],
['red', 'blue', 'green']):
print('{} {} {}'.format(num, color, cheese))
imprime
1 red manchego
2 blue stilton
3 green brie