zip в Python: несколько последовательностей параллельно
Два списка идут параллельно: имена и баллы, товары и цены. zip склеивает их в пары, цикл берёт по одной — без индексов и range(len(...)).
Редакция Питоники
Два списка, которые ходят парой: имена и баллы, товары и цены, города и температуры. Обойти их за один заход — задача каждого второго скрипта. Штатное решение — индексы; у Python есть идиома короче: встроенная функция zip. Разбираем пары, обрезку, строгий режим и словарь из двух списков.
Параллельный проход по индексам
Сначала так, как пишут без zip — через номера позиций:
names = ['Аня', 'Борис', 'Вера']
scores = [82, 91, 75]
for i in range(len(names)):
print(names[i], '-', scores[i])
Аня - 82 Борис - 91 Вера - 75
Работает: range(len(names)) даёт номера, по ним достаём оба значения. Но запись шумит: i, i, i, квадратные скобки в каждой строке. Индекс здесь нужен не сам по себе — он лишь способ достать пару значений. Значит, можно обойтись без него.
zip: пары из двух списков
names = ['Аня', 'Борис', 'Вера']
scores = [82, 91, 75]
for name, score in zip(names, scores):
print(name, '-', score)
Аня - 82 Борис - 91 Вера - 75
zip взял по одному элементу из каждого списка, склеил пару и отдал циклу, а распаковка в заголовке — for name, score in ... — разложила пару на две переменные. Индексы исчезли: читается по-человечески, для каждого имени и балла — печать.
Посмотрим на zip без цикла — что именно он отдаёт:
names = ['Аня', 'Борис', 'Вера']
scores = [82, 91, 75]
pairs = list(zip(names, scores))
print(pairs)
print(pairs[0])
[('Аня', 82), ('Борис', 91), ('Вера', 75)]
('Аня', 82)zip отдаёт пары-кортежи; list разворачивает их в список, чтобы напечатать. Распаковка пар в заголовке цикла — та же, что при переборе словарей в третьем уроке: две переменные через запятую, Python сам разложит кортеж по ним.
Обрезка по самому короткому
days = ['пн', 'вт', 'ср', 'чт', 'пт']
temps = [21, 19, 23]
for day, temp in zip(days, temps):
print(day, temp)
пн 21 вт 19 ср 23
Пять дней, три температуры — пар вышло три. zip останавливается по самому короткому списку: лишний хвост длинного списка просто не попадает в пары. Ни ошибки, ни предупреждения — python-zip-обрезка работает молча.
Три списка и больше
cities = ['Сочи', 'Казань', 'Пермь']
temps = [24, 17, 8]
rains = [False, True, False]
for city, temp, rain in zip(cities, temps, rains):
if rain:
advice = 'зонт'
else:
advice = 'очки'
print(city, temp, '-', advice)
Сочи 24 - очки Казань 17 - зонт Пермь 8 - очки
Аргументов у zip столько, сколько последовательностей, — и столько же переменных в заголовке цикла. Распаковка работает и здесь: город, температура, дождь — три переменных за одну итерацию.
zip с enumerate: номер и пара
Две задачи накладываются друг на друга чаще, чем кажется: нужны и пары, и номер строки. Нумерованный чек, отчёт с колонкой позиции, список задач с номерами — всё это zip и enumerate в одной строке. Синтаксис выглядит страшнее, чем есть, но разбирается за минуту.
names = ['Аня', 'Борис', 'Вера']
scores = [82, 91, 75]
for i, (name, score) in enumerate(zip(names, scores), start=1):
print(i, name, score)
1 Аня 82 2 Борис 91 3 Вера 75
Читается справа налево. zip склеивает имена с баллами в пары, enumerate нумерует пары, а распаковка в скобках разбирает каждую пару на имя и балл. Скобки здесь обязательны: без них Python попытается разложить сразу три значения, а пара — это одна величина.
Словарь из двух списков
goods = ['молоко', 'хлеб', 'сыр']
prices = [89, 42, 590]
menu = dict(zip(goods, prices))
print(menu)
print(menu['сыр'])
{'молоко': 89, 'хлеб': 42, 'сыр': 590}
590Связка dict(zip(...)) собирает словарь одним вызовом: список ключей и список значений превращаются в пары, пары — в словарь. Так колонки файла или формы превращаются в словарь за одну строку; о словарях подробнее — в разделе про словари.
Обрезка внутри dict(zip(...))
Связка dict(zip(keys, values)) выглядит безобидно, но унаследовала все свойства zip, включая тихую обрезку. Если ключей больше, чем значений, лишние ключи просто не попадут в словарь — и никакого сообщения об этом не будет.
keys = ['a', 'b', 'c']
vals = [1, 2]
d = dict(zip(keys, vals))
print(d)
{'a': 1, 'b': 2}Ключа 'c' в словаре нет, и zip не сказал ни слова: пара просто не была создана. Для данных из формы или CSV это означает потерю целой записи, причём заметить её можно только по итогам. Если равные длины обязательны, проверяйте их до сборки: if len(keys) != len(values): — и разбирайтесь с расхождением.
zip одноразовый
a = [1, 2, 3]
b = [4, 5, 6]
pairs = zip(a, b)
print(list(pairs))
print(list(pairs))
[(1, 4), (2, 5), (3, 6)] []
Второй print напечатал пустые скобки: zip — итератор, пары вынимаются из него один раз, как вода из бутылки. Понадобился второй проход — соберите пары в список заранее: pairs = list(zip(a, b)) — и ходите по pairs сколько угодно раз.
Для нумерованного параллельного прохода zip объединяется с enumerate: enumerate(zip(names, scores), start=1) отдаёт номер и пару, а пара распаковывается в скобках: for i, (name, score) in .... С индексами через range так аккуратно не получится — а для сумм по парам пригодится урок про аккумуляторы.
# Имена и баллы вводит пользователь
names = input('Имена через пробел: ').split()
scores = input('Баллы через пробел: ').split()
for name, score in zip(names, scores):
print(name, '-', score)
zip в расчётах
Пара удобна тем, что её не нужно разбирать: два значения пришли вместе и работают вместе. Классика — цена и количество: сумма заказа считается прямо в цикле, без обращения к спискам по индексу.
prices = [89, 42, 590]
qty = [2, 1, 1]
total = 0
for p, q in zip(prices, qty):
total = total + p * q
print(total)
810
810 — это 89 × 2 плюс 42 × 1 плюс 590 × 1. Та же арифметика через range(len(...)) заняла бы больше места и выглядела бы хуже, а ошибка в индексе стоила бы дороже. Единственное, о чём стоит помнить: если длины списков разные, zip возьмёт меньшую и часть позиций не попадёт в расчёт.
zip работает и со строками — символы одной строки склеиваются с символами другой:
letters = ['а', 'б', 'в']
numbers = [1, 2, 3]
for ch, n in zip(letters, numbers):
print(f'{n}) {ch}')
1) а 2) б 3) в
Здесь пары склеиваются из разнородных наборов: буквы и номера. Ничего сверхъестественного в zip нет — это просто способ пройти по нескольким последовательностям синхронно, держа в руках по одному элементу из каждой.
Что выбрать: индексы, zip или пара с номером
Три способа пройти по двум спискам дают одинаковый результат, и выбор между ними — вопрос вкуса, а не правоты. Моя рекомендация: берите zip, когда вам нужны сами значения, и добавляйте enumerate только тогда, когда номер действительно используется в выводе или в логике. Лишний номер в заголовке — это лишний код, который придётся читать.
| Задача | Что писать | Почему |
|---|---|---|
| Значения из двух списков | for a, b in zip(x, y) | пары приходят готовыми, индексы не нужны |
| Значения и их номера | for i, (a, b) in enumerate(zip(x, y), start=1) | номер нужен для вывода или отчёта |
| Только индексы | for i in range(len(x)) | значения по номеру не нужны |
| Словарь из двух списков | dict(zip(keys, values)) | пары превращаются в словарь одним вызовом |
| Разные длины, терять нельзя | zip_longest из itertools | хвост добивается, а не отбрасывается |
Правило простое: индекс — это способ достать значение, а не цель. Если значение можно получить сразу, берите zip. Возвращаться к range(len(...)) стоит только там, где нужен именно номер — например, чтобы обратиться к третьему списку по той же позиции.
| range(len(...)) | zip | |
|---|---|---|
| Что перебираем | номера, значения достаём по ним | пары значений сразу |
| Три списка и больше | скобки растут: a[i], b[i], c[i] | столько же переменных в заголовке |
| Разные длины | IndexError по длинному списку | тихая обрезка по короткому |
| Читаемость | механическая, через индексы | идиоматичная, без индексов |
Сначала предскажи ответ в голове — это главный навык программиста.
a = [1, 2, 3]
b = ['х', 'у']
for x, y in zip(a, b):
print(x, y)
keys = ['a', 'b', 'c']
vals = [1, 2, 3]
d = dict(zip(keys, vals))
print(d['b'] + d['c'])
z = zip([1, 2], [3, 4])
print(list(z))
print(list(z))
1. Сколько пар отдаст zip для списков длиной 5 и 2?
2. Как увидеть пары zip обычным списком?
3. Что получит второй цикл по одному и тому же объекту zip?
4. Что делает zip(a, b, strict=True) в Python 3.10 и новее?
5. Как выглядит заголовок цикла для трёх списков через zip?
6. Как получить и номер строки, и пару из двух списков?
7. Что произойдёт с dict(zip(keys, values)), если ключей больше, чем значений?
8. Зачем в цикле по парам нужен аккумулятор?
Даны списки subjects = ['матем', 'физика'] и grades = [5, 4]. Соберите словарь journal через dict и zip, выведите словарь целиком, затем выведите оценку по физике по ключу.
Что делает zip в Python простыми словами?
Склеивает несколько списков в пары: берёт по одному элементу из каждого списка и отдаёт их вместе. Цикл for name, score in zip(names, scores) за одну итерацию получает и имя, и балл — без индексов и range(len(...)).
Почему zip теряет элементы из длинного списка?
Он отдаёт пары, пока хватает элементов в самом коротком списке, — так устроен по умолчанию. Если равные длины обязательны, включите zip(a, b, strict=True) с Python 3.10: несовпадение длин уронит ValueError вместо тихой потери данных.
Как пройти по двум спискам и всё же получить индексы?
Объединить enumerate и zip: for i, (name, score) in enumerate(zip(names, scores)) — номер и пара за одну итерацию. Нумерация начинается с нуля, а start=1 превратит её в обычный счёт с единицы.
Урок 4: enumerateЧем zip отличается от zip_longest?
zip_longest из стандартного модуля itertools не обрезает, а добивает короткий список значением fillvalue (по умолчанию None): пары идут до конца самого длинного списка. Выбирайте zip для равных по смыслу длин и zip_longest, когда терять хвост нельзя.
Как пронумеровать пары из двух списков?
Соединить zip и enumerate: for i, (name, score) in enumerate(zip(names, scores), start=1). Скобки вокруг пары обязательны, иначе Python не сможет разложить три значения.
Урок 4: enumerateПочему словарь из zip может не совпасть с исходными данными?
zip обрезает по самому короткому списку, и dict(zip(keys, values)) ведёт себя так же: лишние ключи просто не попадут в словарь, без всякого сообщения. Проверяйте длины или включайте strict=True.
Понравился урок? Сошлитесь на него
«zip останавливается по самому короткому списку: лишний хвост длинного списка просто не попадает в пары.»
Скопируйте готовую ссылку в формате HTML, Markdown или чистый адрес и вставьте в статью на Habr, VC, Telegram-канал или свой блог — так о проекте узнают новые читатели.
Что дальше
Осталось в разделе: 20 из 20. Прогресс хранится в вашем браузере.
Что читать дальше
Циклы · Урок 3
Python цикл по строке, словарю и строкам файла
for ходит не только по спискам: строка — последовательность символов, словарь перебирается по ключам, а items отдаёт готовые пары. Плюс построчный перебор текста файла.
Циклы · Урок 4
enumerate в Python: индекс и значение вместе
Нужен номер элемента вместе с самим элементом? enumerate отдаёт циклу пары «индекс — значение»: без ручного счётчика и с нумерацией с единицы через start=1.
Циклы · Урок 11
Счётчики и аккумуляторы в циклах Python
Сумма чека, число совпадений, лучший результат: переменная, объявленная до цикла и копящая значения внутри, — приём старше языков. Разбираем счётчики, суммы, максимумы и их ловушки.
Похожие уроки по темам
Подобраны автоматически по пересечению тем и ключевых слов.
Множества · Урок 13
Распаковка кортежей в Python: x, y = point
Один кортеж — несколько переменных за один шаг: x, y = point. Классический обмен значений, звёздочка для остатка и почему распаковывать множества опасно.
python распаковка кортежейраспаковка кортежа python
Словари · Урок 8
Перебор словаря: keys, values и items
Три представления словаря — keys, values, items: перебираем цены в цикле for, распаковываем пары в две переменные, считаем сумму и фильтруем дешёвые позиции.
python dict itemsперебор словаря python
Циклы · Урок 1
Цикл for в Python: первый цикл
Первый цикл for: компьютер повторяет одну строку за вас. Перебираем список, разбираем переменную цикла и тело с отступами — и видим, зачем цикл вместо копипасты.
цикл for pythonpython цикл for