UpPetto

This is a real course generated by UpPetto — unedited.

AI-generated. Two models wrote it, a third checked every risky claim against sources, a fourth re-checked.

Create my own course

enumerate, zip и готовые абстракции itertools/collections

optional заменить ручной range(len(...)) и параллельные индексы на enumerate/zip и подобрать функцию из itertools/collections для типовой задачи

Параллельная итерация по нескольким массивам, обход с индексом и подсчёт с группировкой — задачи, которые в C решаются вручную: общий индекс i для двух массивов, отдельная переменная-счётчик, самописная логика группировки в массив структур. В Python эти паттерны настолько типичны, что закрыты готовыми функциями встроенного набора и стандартной библиотеки; писать их руками — верный признак «C с питоновским синтаксисом».

enumerate: итерация с индексом

Проблема: нужен и индекс, и сам элемент. C-подобный анти-паттерн:

for i in range(len(items)):
    print(f"Index: {i}, Value: {items[i]}")

Идиоматично: enumerate оборачивает итерируемый объект и на каждом шаге отдаёт кортеж (индекс, элемент), так что индекс и значение невозможно рассинхронизировать:

for i, item in enumerate(items):
    print(f"Index: {i}, Value: {item}")

Начальное значение счётчика задаётся вторым аргументом: enumerate(items, 1) нумерует с единицы — то, что в C вы бы писали как i + 1 внутри цикла.

zip: параллельная итерация

Проблема: нужно идти по нескольким коллекциям одновременно. C-подобный анти-паттерн — общий индекс, который вы сами обязаны согласовать с длинами обоих списков:

for i in range(len(names)):   # опасно, если длины не совпадают
    print(f"{names[i]} is {ages[i]} years old.")

Идиоматично: zip «сшивает» несколько итерируемых объектов и на каждом шаге отдаёт кортеж из их элементов. zip останавливается, как только заканчивается самая короткая последовательность, — поэтому IndexError из-за разной длины здесь просто невозможен:

for name, age in zip(names, ages):
    print(f"{name} is {age} years old.")

Если нужно идти до конца самой длинной последовательности, дополняя недостающие значения, берите itertools.zip_longest. Если нужны сразу индекс и пара значений — enumerate(zip(a, b)), а не два счётчика.

Подсчёт и группировка

Ручной подсчёт «если ключа нет — создать, иначе увеличить» и ручная группировка в стиле «массив структур» тоже уже написаны за вас в модуле collections:

from collections import Counter, defaultdict

counts = Counter(words)                    # частоты элементов
by_city = defaultdict(list)
for user in users:
    by_city[user["city"]].append(user["name"])   # группировка без проверки наличия ключа

defaultdict(list) создаёт пустой список при первом обращении к новому ключу — это заменяет привычную C-проверку «инициализирован ли слот». Обычный dict даёт то же через setdefault, а для чтения с значением по умолчанию есть get.

Общий принцип: перед тем как писать цикл со счётчиками, проверьте, нет ли для этой задачи готовой функции — во встроенных именах (enumerate, zip, sum, min, max, sorted, any, all) или в модулях collections, itertools, functools.

Разобранный пример

Задача: вывести таблицу «номер строки — имя — балл» по двум параллельным спискам.

Перенесённый C-стиль — один индекс на две коллекции:

names = ["Alice", "Bob", "Charlie"]
scores = [90, 75, 82]

for i in range(len(names)):
    print(i + 1, names[i], scores[i])

Здесь три источника ошибок: range(len(...)), ручное i + 1 для нумерации с единицы и молчаливое предположение, что scores не короче names.

Идиоматично:

for rank, (name, score) in enumerate(zip(names, scores), 1):
    print(rank, name, score)

Ни одной переменной-индекса: zip отвечает за согласование коллекций, enumerate — за нумерацию. Если scores окажется короче, цикл просто закончится раньше, а не упадёт с IndexError на несуществующем индексе.

Попробуй сейчас

Дан код в C-стиле:

temps = [18, 21, 25, 19]
cities = ["Kyiv", "Lviv", "Odesa", "Kharkiv"]

hot = {}
for i in range(len(cities)):
    if temps[i] >= 20:
        hot[cities[i]] = temps[i]

for i in range(len(cities)):
    print(str(i + 1) + ". " + cities[i])

Перепишите оба цикла без range(len(...)): первый — через zip (можно одним dict comprehension), второй — через enumerate с нумерацией с единицы.

Получилось, если…

Вы справились, если:

  • первый цикл превратился в {city: t for city, t in zip(cities, temps) if t >= 20} (или в обычный for city, t in zip(...));
  • второй — в for n, city in enumerate(cities, 1): print(n, city);
  • вы можете объяснить, что произойдёт, если temps окажется короче cities: zip остановится по короткой последовательности, тогда как исходный код обратился бы к несуществующему индексу и упал с IndexError.

Вывод

enumerate даёт индекс без range(len(...)), zip — параллельный обход без общего счётчика и без риска выйти за границы (он останавливается по самой короткой последовательности; для обхода по самой длинной есть itertools.zip_longest). Для подсчёта и группировки берите Counter и defaultdict вместо ручных проверок наличия ключа.

AI-generated · source-grounded review

🛡 Fact-checked: 3 risky claims verified · 1 removed · confidence: medium · a second model checked these claims and agreed
[verified] zip останавливается по самой короткой последовательности; itertools.zip_longest идёт до самой длинной
База подтверждает zip как параллельное объединение последовательностей и наличие модуля itertools
[verified] enumerate добавляет счётчик и принимает начальное значение вторым аргументом
База: enumerate добавляет счётчик к элементам без ручного range(len(...))
[verified] Counter и defaultdict из collections заменяют ручной подсчёт и группировку
База: модуль collections предоставляет дополнительные абстракции для операций с данными
[removed] Оба исходных варианта урока 5 обрывались на середине (в версии B — незакрытая figure-svg с enumerate); незавершённая фигура не переносилась, так как её содержимое невозможно проверить
Key concepts: enumerate zip itertools collections
Tell me more 🔒 Didn't understand — explain simply 🔒 Show examples 🔒 Sources 🔒

On your own course these buttons answer instantly, quizzes track what you've mastered, and lessons adapt to your gaps. Write my course

Check yourself

1. Что возвращает enumerate(items) на каждой итерации?
2. Что произойдёт при `for a, b in zip(x, y)`, если списки x и y разной длины?
3. Какой инструмент избавляет от ручной проверки «есть ли уже такой ключ» при группировке значений по ключу?
On your own course, these are marked as you answer