Как перебрать два списка одновременно в Python (zip)?
У меня два списка одинаковой длины — имена и оценки. Хочу пройти их в одном цикле, чтобы на каждом шаге был и элемент из первого, и из второго. Сейчас бегаю по индексам через range(len()), но выглядит громоздко. Как итерировать два списка параллельно?
2 ответа
Идеальный инструмент — zip. Он спаривает элементы двух (или больше) списков по позициям, и в цикле вы сразу получаете пару:
names = ['Аня', 'Боря', 'Вера']
scores = [5, 4, 3]
for name, score in zip(names, scores):
print(f'{name}: {score}')
# Аня: 5
# Боря: 4
# Вера: 3
Это чище, чем for i in range(len(names)): с обращениями names[i], scores[i] — меньше шансов перепутать индекс и не нужно следить за длиной.
Списков может быть и больше двух:
for a, b, c in zip(list1, list2, list3):
...
Главная ловушка — разная длина. zip молча обрезает по самому короткому списку, без ошибки:
for x, y in zip([1, 2, 3], ['a', 'b']):
print(x, y) # только (1,'a') и (2,'b'); тройка потеряна
Если потеря данных недопустима, в Python 3.10+ есть zip(a, b, strict=True) — он бросит ValueError, если длины не совпали. А если, наоборот, нужно дойти до конца длинного списка, подставляя заглушки, берите itertools.zip_longest.
И если нужен ещё индекс — комбинируйте с enumerate: for i, (name, score) in enumerate(zip(names, scores)):.
Маленькое дополнение: zip возвращает ленивый итератор, а не список. В цикле это не мешает, но если нужен именно список пар — оберните в list():
pairs = list(zip(names, scores)) # [('Аня', 5), ('Боря', 4), ('Вера', 3)]
И обратная операция — распаковка через zip(*pairs) «разъединяет» пары обратно в два кортежа. Это тот же zip, просто с распаковкой звёздочкой.