Перейти к содержанию

432 уроков, 26 библиотек и челлендж «Что выведет код?» — бесплатно, код прямо в браузере

Начать обучение
Урок 4 из 20 Начальный 15 мин 100 XP

enumerate в Python: индекс и значение вместе

Нужен номер элемента вместе с самим элементом? enumerate отдаёт циклу пары «индекс — значение»: без ручного счётчика и с нумерацией с единицы через start=1.

Редакция Питоники

Цикл из первого урока отдаёт элементы, но не их номера. А номера нужны постоянно: «1. Анна» в списке участников, «строка 3» в отчёте, «позиция 2» в чеке. Классическое решение — ручной счётчик: переменная, которую вы объявляете до цикла и увеличиваете внутри. Оно работает, но занимает три строки и оставляет три шанса ошибиться. В Python есть встроенная функция enumerate, которая отдаёт номер и значение вместе.

Ручной счётчик: работает, но шумит

Сначала — как нумеруют без enumerate, чтобы было с чем сравнивать:

нумерация вручную
i = 1
for name in ['Анна', 'Борис', 'Вера']:
    print(f'{i}. {name}')
    i = i + 1
Вывод
1. Анна
2. Борис
3. Вера

Вывод верный, но цена высока. Три заботы: объявить i = 1 до цикла, не забыть i = i + 1 внутри и увеличить строго в нужном месте — строка инкремента, потерявшаяся в длинном теле, ломает всю нумерацию. Забытый счётчик даст одинаковые номера на каждой строке. Всё это — ручная бухгалтерия вокруг того, что Python умеет считать сам.

enumerate: номер и элемент парой

enumerate оборачивает последовательность и отдаёт циклу пары: номер и элемент. Два имени в заголовке распаковывают пару — номер в i, элемент в name:

enumerate вместо счётчика
names = ['Анна', 'Борис', 'Вера']

for i, name in enumerate(names):
    print(f'{i}. {name}')
Вывод
0. Анна
1. Борис
2. Вера

Никаких объявлений до цикла и инкрементов внутри — нумерация живёт в заголовке. Обратите внимание: нумерация начинается с нуля, как индексы списка: Анна — элемент с номером 0. То есть i здесь — это честный индекс: по нему можно обращаться к элементу, он совпадает с позициями срезов и range. enumerate отдаёт пары: номер и элемент вместе, и нумерация по умолчанию идёт с нуля.

start=1: нумерация с единицы

Для людей нумерация с нуля непривычна: списки покупок и чеки начинаются с единицы. Аргумент start задаёт, с какого номера начинать отсчёт:

нумерация с единицы
names = ['Анна', 'Борис', 'Вера']

for num, name in enumerate(names, start=1):
    print(f'{num}. {name}')
Вывод
1. Анна
2. Борис
3. Вера

Порядок перебора не изменился — элементы идут в прежней последовательности. start влияет только на числа, которые видит ваша печать: первый элемент получит номер 1 вместо 0. Сам список от этого не двигается, индексы остаются 0, 1, 2 — запомним это для питфолла ниже.

Что отдаёт enumerate без распаковки

Чтобы увидеть механизм, напечатаем пары целиком, без распаковки в две переменные:

пары целиком
names = ['Анна', 'Борис']

for pair in enumerate(names):
    print(pair)
Вывод
(0, 'Анна')
(1, 'Борис')

Каждый шаг отдаёт кортеж из двух элементов: (0, 'Анна') — номер и значение. Распаковка for i, name in ... лишь раскладывает этот кортеж по двум переменным, как мы делали с items() в уроке про перебор словаря. Скобки в печати — подсказка: если в переменной кортеж, а вы ждали число, значит распаковки нет.

Номер, который начинается не с нуля

Аргумент start умеет больше, чем просто сдвинуть счёт на единицу. В него можно положить любое целое число, и отсчёт пойдёт от него: так строят нумерацию с конца, с обратного знака или с произвольного смещения.

нумерация с конца
for num, ch in enumerate('Кот', start=-2):
    print(num, ch)
Вывод
-2 К
-1 о
0 т

Три буквы получили номера -2, -1 и 0 — отсчёт идёт к нулю с конца. Так выглядит позиция символа относительно середины строки: у слова из трёх букв средняя буква имеет номер 0. Для обычных задач это редкость, но знать полезно: start — это просто начало отсчёта, а не флаг «с единицы».

Когда номер реально нужен

Соблазн не пользоваться enumerate велик: номер почти никогда не нужен, и его добавление выглядит лишним. Но есть случаи, где без номера не обойтись, и вот они встречаются постоянно. Первый — скидки по ступеням: цена падает на каждом шаге, и шаг должен быть виден в выводе.

ступенчатая скидка
price = 1500
discounts = [0, 100, 300]
for step, off in enumerate(discounts):
    print(f'шаг {step}: цена {price - off}')
Вывод
шаг 0: цена 1500
шаг 1: цена 1400
шаг 2: цена 1200

Здесь номер — не украшение, а часть данных: он показывает, на каком шаге скидка применяется. Сравните с обычным циклом: без номера пришлось бы заводить счётчик вручную, а потом ещё объяснять, что он означает.

Второй случай — словарь позиций. Когда элементы имеют смысл только вместе со своим местом, индекс становится ключом:

словарь позиций
words = ['кот', 'пароход', 'ёж']
positions = dict(enumerate(words))
print(positions)
print(positions[1])
Вывод
{0: 'кот', 1: 'пароход', 2: 'ёж'}
пароход

Получился словарь «номер — слово». Теперь по номеру можно достать элемент за одно обращение, без поиска по списку. Тот же приём с двумя списками разбирается в уроке про zip — там пары склеиваются, а здесь нумеруются.

Нумерованный чек: распаковка в два этажа

Если элементы сами являются парами — например, товары с ценами, — распаковка вкладывается: два имени для enumerate и скобки для вложенной пары:

чек с позициями
cart = [('Хлеб', 42), ('Молоко', 89)]

for num, (name, price) in enumerate(cart, start=1):
    print(f'{num}. {name} — {price} р.')
Вывод
1. Хлеб — 42 р.
2. Молоко — 89 р.

num получает позицию, скобки (name, price) распаковывают вложенный кортеж товара. Так собираются настоящие отчёты: нумерованные списки, построчные чеки, оглавления.

опасная смесь номера с индексом
names = ['Анна', 'Борис', 'Вера']

for num, name in enumerate(names, start=1):
    print(names[num])
Не запускается: на третьем шаге num равен 3, а имен с индексом 3 нет — IndexError: list index out of range. Элемент уже лежит в name: с ним и работайте.

Нумерация и пары: enumerate вместе с zip

Две задачи — «нужен номер» и «нужны пары из двух списков» — обычно встречаются вместе: пронумерованный чек, отчёт с колонкой позиции, таблица с порядковым номером. Складывать enumerate и zip можно напрямую, но порядок имён в заголовке меняется, и это место, где новички спотыкаются чаще всего.

номер и пара
names = ['Аня', 'Борис']
scores = [82, 91]
for i, (name, score) in enumerate(zip(names, scores), start=1):
    print(i, name, score)
Вывод
1 Аня 82
2 Борис 91

Читается справа налево: сначала zip склеивает имена с баллами в пары, потом enumerate нумерует эти пары, а распаковка в скобках разбирает каждую пару на два значения. Скобки обязательны — без них Python попытается распаковать в i и name сразу два элемента, а это уже не пара, а всё вместе.

enumerate по строке и до него: range(len())

enumerate работает по любой последовательности — и по строке тоже: номера достанутся символам:

enumerate по строке
for i, ch in enumerate('Кот'):
    print(i, ch)
Вывод
0 К
1 о
2 т

А вот как в старом стиле нумеруют через диапазон: range(len(names)) даёт индексы, и элемент достаётся по индексу из списка:

старый стиль: range(len(...))
names = ['Анна', 'Борис', 'Вера']

for i in range(len(names)):
    print(f'{i + 1}. {names[i]}')
Вывод
1. Анна
2. Борис
3. Вера

Результат тот же, что у enumerate с start=1, — но обратите внимание на цену: i + 1 вручную и names[i] вместо имени элемента. Такой код встречается в чужих программах, читать его полезно, писать — уже не обязательно: enumerate короче и яснее. Диапазоны и их границы разобраны в уроке про range.

Ручной счётчик / range(len)enumerate
Счётчик объявляется и увеличивается вручнуюНомер живёт в заголовке цикла
names[i] вместо имени элементаЭлемент сразу в своей переменной
Инкремент можно забыть или сдвинуть в телеЗабыть некуда: нумерация встроена
start=1 требует i + 1 на каждом шагеstart=1 задаётся одним аргументом
  • нумерованные отчёты и списки: enumerate(..., start=1) даёт человеческую нумерацию;
  • позиции в данных: где встретился нужный символ, на какой строке файла ошибка — номера нужны и при отладке;
  • выравнивание номеров в колонках делают f-строки с шириной — формат-спецификации f-строк показывают как;
  • когда номер не нужен, а элемент есть — enumerate лишний: обычный for из первого урока делает ту же работу короче.

Теперь цикл умеет и данные, и счёт. Следующий урок добавляет новый вид цикла — while: повторять не «по элементам», а «пока верно условие», когда число шагов заранее неизвестно.

Что выведет код?

Сначала предскажи ответ в голове — это главный навык программиста.

for i, ch in enumerate('аб'):
    print(i, ch)
for num, x in enumerate(['a', 'b'], start=1):
    print(num, x)
for pair in enumerate(['x']):
    print(pair)
Проверь себя
0 / 9

1. Что отдаёт enumerate(['a', 'b']) на каждом шаге?

2. С какого числа enumerate(names) начинает нумерацию по умолчанию?

3. Что делает аргумент start в enumerate(names, start=1)?

4. Почему обращение names[num] при enumerate(names, start=1) опасно?

5. Чем enumerate лучше ручного счётчика i = i + 1?

6. Что лежит в переменной цикла при for pair in enumerate(names)?

7. Что даст enumerate('Кот', start=-2)?

8. Как правильно пронумеровать пары из zip?

9. Зачем нужен enumerate, если номер почти никогда не требуется?

Карточки терминов
Запомнено: 0 / 9
Практика

Для списка дел ['Размяться', 'Позавтракать', 'Выйти'] напечатай нумерованный список с единицы: «1. Размяться» и так далее. Используй enumerate.

practice.py
Вопросы и ответы по уроку

Что делает enumerate в Python?

Отдаёт циклу пары «номер — элемент»: for i, name in enumerate(names) на каждом шаге кладёт в i позицию, а в name — сам элемент. Нумерация идёт с нуля и совпадает с индексами списка.

Урок 1: первый цикл for

Как нумеровать цикл с единицы, а не с нуля?

Передать start: enumerate(names, start=1) начнёт номера с единицы. Важно: start меняет только печатаемые числа — индексы списка остаются с нуля, поэтому names[num] со start=1 на последнем шаге упадёт с IndexError.

Зачем enumerate, если есть range(len(names))?

range(len(...)) даёт только индексы, и элемент приходится доставать как names[i] на каждом шаге. enumerate отдаёт элемент сразу в своей переменной — код короче и без лишнего обращения к списку. range(len) остаётся для случаев, когда нужен именно перебор номеров без значений.

Урок 2: range — цикл со счётчиком

Почему enumerate печатает (0, 'Анна') со скобками?

Это кортеж — пара из номера и элемента, напечатанная без распаковки. Два имени в заголовке цикла (for i, name in ...) раскладывают кортеж по переменным, и печать станет обычной: номера и значения без скобок.

Как получить номер элемента и его значение вместе с zip?

Соединить enumerate и zip: for i, (name, score) in enumerate(zip(names, scores), start=1). Скобки вокруг name, score обязательны — пара распаковывается как одна величина, и без скобок будет TypeError.

Урок 10: zip — несколько последовательностей

Можно ли нумеровать не с нуля, а, например, с минуса?

Да: start принимает любое целое число. enumerate('Кот', start=-2) выдаст номера -2, -1, 0 — удобно, когда позиция считается относительно середины или конца.

Понравился урок? Сошлитесь на него

«enumerate отдаёт пары: номер и элемент вместе, и нумерация по умолчанию идёт с нуля.»

Скопируйте готовую ссылку в формате HTML, Markdown или чистый адрес и вставьте в статью на Habr, VC, Telegram-канал или свой блог — так о проекте узнают новые читатели.

TelegramVK

Что дальше

Осталось в разделе: 20 из 20. Прогресс хранится в вашем браузере.

Похожие уроки по темам

Подобраны автоматически по пересечению тем и ключевых слов.