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:
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])
Нумерация и пары: 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 работает по любой последовательности — и по строке тоже: номера достанутся символам:
for i, ch in enumerate('Кот'):
print(i, ch)
0 К 1 о 2 т
А вот как в старом стиле нумеруют через диапазон: range(len(names)) даёт индексы, и элемент достаётся по индексу из списка:
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)
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, если номер почти никогда не требуется?
Для списка дел ['Размяться', 'Позавтракать', 'Выйти'] напечатай нумерованный список с единицы: «1. Размяться» и так далее. Используй enumerate.
Что делает 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-канал или свой блог — так о проекте узнают новые читатели.
Что дальше
Осталось в разделе: 20 из 20. Прогресс хранится в вашем браузере.
Что читать дальше
Циклы · Урок 1
Цикл for в Python: первый цикл
Первый цикл for: компьютер повторяет одну строку за вас. Перебираем список, разбираем переменную цикла и тело с отступами — и видим, зачем цикл вместо копипасты.
Циклы · Урок 2
range в Python: цикл со счётчиком
range делает цикл for счётчиком: n повторов от нуля, диапазон от начала до конца, шаг и обратный отсчёт. Разбираем все три формы range на примерах с запуском.
Циклы · Урок 3
Python цикл по строке, словарю и строкам файла
for ходит не только по спискам: строка — последовательность символов, словарь перебирается по ключам, а items отдаёт готовые пары. Плюс построчный перебор текста файла.
Похожие уроки по темам
Подобраны автоматически по пересечению тем и ключевых слов.
NumPy · Урок 3
Индексация и срезы массивов NumPy: как достать любой элемент
Индексы с нуля и с конца, срезы с шагом, строки и столбцы двумерных массивов — и главный подводный камень NumPy: срез возвращает view, а не копию. С примерами, разбором ошибок и шпаргалкой.
numpy индекс элементаnumpy start stop step
json · Урок 10
Обход данных: циклы по JSON-спискам
for по списку словарей, извлечение полей, суммирование выручки и list comprehension — превращаем выгрузку заказов в отчёт.
python цикл по jsonpython цикл по orders
Циклы · Урок 9
else у циклов в Python: поиск без флагов
У циклов for и while есть собственный else: он срабатывает, если перебор дошёл до конца без break. Поиск «нашли или нет» — в один заход и без флагов.
python цикл elseцикл for с else