Say you have the following collections declared

c1 = [1, 2, 3]
c2 = ["one", "two", "three", "four"]


and you need to iterate them both at the same time. It’s fairly easy with zip function:

for idx, name in zip(c1, c2):
print(idx)
print(name)


outputs

1
one
2
two
3
three


zip essentially creates a tuple on each iteration, but we are also unpacking the tuple into idx and name for easier access. Of course you can pass more than two collections in zip:

c1 = [1, 2, 3]
c2 = ["one", "two", "three", "four"]
c3 = [True, False]

for idx, name, is_good in zip(c1, c2, c3):
print(idx)
print(name)
print(is_good)

1
one
True
2
two
False


Not that zip iterates as long as there are elements in all collections to create a tuple. In the first example, c1 has 3 elements, and c2 4, but only 3 iterations were made. In the second example with 3 collections only 2 iterations were made. This is sometimes annoying, but python standard library has a solution as well - itertools.zip_longest:

import itertools
for idx, name, is_good in itertools.zip_longest(c1, c2, c3, fillvalue="<empty>"):
print(idx)
print(name)
print(is_good)

1
one
True
2
two
False
3
three
<empty>
<empty>
four
<empty>


fillvalue parameter is optional and only will kick in when a collection has no value (it’s shorter than others). When not specified, it’s set to None.