На этом
дополнительном занятии я расскажу о функции enumerate. Прежде чем мы углубимся
в эту тему, напомню, что в Python для
последовательного перебора элементов любого итерируемого объекта используется
оператор цикла for. Например:
a = [1, 4, 2, -5, 0, 11]
for x in a:
print(x)
В момент работы
этого цикла переход к следующему элементу осуществляется через объект-итератор.
Оператор for автоматически
вызывает функцию next() этого итератора и получает текущее значение
элемента. И так, пока не дойдет до конца коллекции:
В результате, x – это лишь
ссылка на текущий элемент списка и, как мы уже говорили, если попытаться
изменить значение коллекции через x, то ничего не произойдет, т.к.
мы лишь изменим саму ссылку x.
Если же внутри
цикла требуется не только перебирать элементы, но и менять их, то необходимо
обращаться к текущему элементу по его индексу, например, так:
i = 0
for x in a:
if x % 2 == 0:
a[i] += 1
i += 1
print(a)
Результатом
будет измененная коллекция:
[1, 5, 3, -5, 1,
11]
Однако, если
вместо a[i] использовать x, то
упорядоченный список останется без изменений. Этот простой пример наглядно
показывает необходимость использования индексов, если нам нужно менять значения
элементов. Так вот, чтобы непосредственно в цикле for не только
получать ссылку на текущий элемент, но и знать его порядковый индекс, как раз и
используется функция enumerate.
Перепишем наш
пример с использованием этой функции:
for i, x in enumerate(a):
if x % 2 == 0:
a[i] += 1
print(a)
Смотрите, у
функции enumerate первым параметром указывается перебираемая коллекция (любой
итерируемый объект), а при переборе элементов она выдает кортеж из двух
значений:
(индекс,
значение элемента)
И программа
значительно упрощается и приятнее читается. Давайте еще проще запишем этот
цикл, пусть он просто выводит в консоль то, что возвратит нам эта функция:
for e in enumerate(a):
print(e)
Как видите,
именно кортеж и получаем на каждой итерации работы цикла. То есть, эта функция
используется исключительно для удобства, когда нам нужно не только иметь ссылку
на текущий элемент, но и знать его порядковый индекс.
Вообще функция enumerate имеет следующий
синтаксис:
enumerate(sequence,
start=0)
Здесь sequence – это любой
итерируемый объект; start – начальное значение индекса. Например,
если вторым параметром указать 1, то индекс будет отсчитываться уже от 1:
for e in enumerate(a, 1):
print(e)
И так далее. Давайте
теперь разберемся, что возвращает функция enumerate? Возможно, вас
сейчас удивила эта фраза: я же только что говорил, что на каждой итерации мы
получаем кортеж из двух значений – индекса и самого элемента? Все так, но
ключевым здесь является фраза «на каждой итерации». Сейчас я поясню, что имею в
виду. Смотрите, если просто взять и вызвать эту функцию, а затем, результат
вывести в консоль:
g = enumerate(a)
print( g )
то увидим
сообщение:
<enumerate
object at 0x01B5F628>
Никакого кортежа
здесь нет, g – это
итерируемый объект, то есть, объект-итератор, который можно перебирать с
помощью функции next:
Именно это и
делает цикл for. Он
автоматически вызывает эту функцию, и уже на ее выходе формируется кортеж,
который мы и видим на каждой итерации. Именно так следует воспринимать функцию enumerate, как
объект-итератор. Фактически, ее (в упрощенном виде), можно записать вот так:
def enumerate(sequence, start=0):
n = start
for elem in sequence:
yield n, elem
n += 1
То есть, это
просто дополнительная обертка над итерируемым объектом sequence. Вот что из
себя представляет функция enumerate.
В заключении
отмечу, как эта функция применяется к разным типам объектов:
- для строк:
for i in enumerate("hello"):
print(i)
- для словаря:
c = {1: 'a', 2: 'b', 3: 'c'}
for i in enumerate(c):
print(i)
(на выходе
получим индексы и ключи);
- для множества:
s = {1,2,3,4,5,4,4,4}
for i in enumerate(s):
print(i)
(порядок
следования элементов может отличаться).
Путь кодера
Подвиг 1. Имеется
упорядоченный список:
A = [[1, 2, 3],
[4, 5, 6], [7, 8, 9]]
Перебрать все
элементы этого списка с помощью функций enumerate и элементы,
стоящие на главной диагонали (имеющие равные индексы), превратить в нули.
Подвиг 2. Написать свою
функцию enumerate, которая бы для
словарей возвращала кортеж из трех значений:
(индекс, ключ,
значение)
Для остальных
коллекций работала бы без изменений.
Подвиг 3. Написать свою
функцию enumerate, которая бы
позволяла одним циклом for перебирать
двумерные (вложенные) упорядоченные списки (как в подвиге 1) и возвращала
кортеж из трех значений:
(строка,
столбец, значение)