Функция enumerate

В Python есть еще одна встроенная функция, которая часто используется в заголовке for. Это функция enumerate(). Если range() позволяет получить только индексы элементов списка, то enumerate() – сразу индекс элемента и его значение.

Функция enumerate() применяется для так называемых итерируемых объектов (список относится к таковым) и создает объект-генератор, который генерирует кортежи, состоящие из двух элементов – индекса элемента и самого элемента.

>>> spisok = [16, 46, 26, 36]
>>> for i in enumerate(spisok):
...     print(i)
... 
(0, 16)
(1, 46)
(2, 26)
(3, 36)
>>> b = "hello"
>>> for i in enumerate(b):
...     print(i)
... 
(0, 'h')
(1, 'e')
(2, 'l')
(3, 'l')
(4, 'o')

Эти кортежи можно распаковывать, то есть извлекать индекс и значение, в теле цикла:

>>> for item in enumerate(spisok):
...     print(item[0], item[1])
... 
0 16
1 46
2 26
3 36

Однако чаще это делают еще в заголовке for, используя две переменные перед in:

>>> for id, val in enumerate(spisok):
...     print(id, val)
... 
0 16
1 46
2 26
3 36

Функция enumerate() используется для упрощения прохода по коллекциям в цикле, когда кроме самих элементов требуется их индекс:

>>> a = [10, 20, 30, 40]
>>> for id, item in enumerate(a):
...     a[id] = item + 5
... 
>>> a
[15, 25, 35, 45]

В данном случае на каждой итерации цикла из объекта, полученного от вызова функции enumerate, извлекается очередной кортеж. Этот кортеж состоит из индекса очередного элемента списка и значения этого элемента. Элементы кортежа связываются с идентификаторами id и item.

Без использования enumerate в цикл пришлось бы вводить переменную-счетчик:

>>> a = [10, 20, 30, 40]
>>> id = 0 # используется счетчик
>>> for num in a:
...     a[id] = num + 5 
...     id += 1
... 
>>> a
[15, 25, 35, 45]

Или извлекать элементы по индексу:

>>> a = [10, 20, 30, 40]
>>> for i in range(len(a)): # перебор по индексам
...     a[i] += 5
... 
>>> a
[15, 25, 35, 45]

Другими словами, без функции enumerate можно обойтись. Однако в ряде случаев она может быть удобней.

Если enumerate() так хороша, зачем использовать range() в заголовке for? На самом деле незачем, если только вам так не проще. Кроме того, бывают ситуации, когда значения не нужны, нужны только индексы. Однако следует учитывать один нюанс. Функция enumerate() возвращает так называемый объект-итератор. Когда такие объекты сгенерировали значения, то становятся "пустыми". Второй раз по ним пройтись нельзя.

Функция range() возвращает итерируемый объект. Хотя такой объект может быть превращен в объект-итератор, сам по себе таковым не является.

Когда range() и enumerate() используются в заголовке for, то разницы нет, так как range- и enumerate-объекты не присваиваются переменным и после завершения работы цикла теряются. Но если мы присваиваем эти объекты переменным, их отличия могут сказаться на выполнении программы:

>>> r_obj = range(len(spisok))
>>> e_obj = enumerate(spisok)
>>> for i in r_obj:
...     if i == 1:
...             break
... 
>>> for i in e_obj:
...     if i[0] == 1:
...             break
... 
>>> for i in r_obj:
...     print(i)
... 
0
1
2
3
>>> for i in e_obj:
...     print(i)
... 
(2, 26)
(3, 36)

Сначала мы прерываем извлечение элементов из объектов на элементе с индексом 1. Когда снова прогоняем объекты через цикл for, то в случае r_obj обход начинается сначала, а в случае e_obj продолжается после места останова. Объект e_obj уже не содержит извлеченных ранее элементов.

Практическая работа

Дан список чисел. Используя функцию enumerate() в заголовке цикла for, создайте второй список, в котором каждый элемент должен быть строкой, включающей через пробел индекс и значение соответствующего элемента первого списка.

Примеры решения и дополнительные уроки в android-приложении и pdf-версии курса

Python. Введение в программирование