Цикл for — одна из базовых управляющих конструкций в любом языке программирования. Простейший цикл в языке Си выглядит так:
int i; for (i=0;i<N;i++) { // действия }
В Си нет более элегантного способа написать цикл for. В более сложных сценариях мы обычно прибегаем к вложенным циклам и множеству вспомогательных переменных (например, i
в примере выше).
К счастью, в Python все намного удобнее. У этого языка есть возможности, позволяющие писать лаконичные циклы — это очень упрощает нашу жизнь. В Python можно избежать вложенных циклов, а вспомогательные переменные объявлять вовсе не обязательно. А еще мы сами можем настроить цикл for
!
В этой статье мы расскажем вам о тонкостях написания цикла for
. Надеемся, вы прочувствуете всю красоту Python.
Выводим индекс и значение одновременно
Обыденный сценарий: мы работаем со списком и нам нужно вывести и индексы, и соответствующие значения. Когда я только начал изучать Python, мой код выглядел примерно так:
for i in range(len(my_list)): print(i, my_list[i])
Да, это работает. Но это недостаточно «питонично». Через несколько месяцев изучения Python я решал эту задачу так:
for i, v in enumerate(my_list): print(i, v)
Как видите, встроенная функция enumerate значительно упрощает нашу жизнь.
Избавляемся от вложенных циклов с помощью функции product
Вложенные циклы — настоящая головная боль. Они усложняют не только сам код, но и его читаемость. Выход из этих циклов — задача тоже сложная. Чтобы найти ошибку, приходится приложить много усилий, ведь нужно проверить каждый внутренний цикл.
К счастью, существует очень полезная встроенная функция — product
. Она является частью встроенного модуля Python — itertools
. С ее помощью мы можем избавиться от вложенных циклов.
Рассмотрим пример:
list_a = [1, 2020, 70] list_b = [2, 4, 7, 2000] list_c = [3, 70, 7] for a in list_a: for b in list_b: for c in list_c: if a + b + c == 2077: print(a, b, c) # 70 2000 7
Объявлено три списка чисел. Нам нужно вывести три числа (по одному из каждого списка), сумма которых равна 2077. Чтобы решить эту задачу, нам понадобилось три вложенных цикла. Код выглядит совсем не изящно.
А теперь опробуем функцию product
.
from itertools import product list_a = [1, 2020, 70] list_b = [2, 4, 7, 2000] list_c = [3, 70, 7] for a, b, c in product(list_a, list_b, list_c): if a + b + c == 2077: print(a, b, c) # 70 2000 7
Как видите, с помощью функции product количество циклов сокращается до одного.
Функция product
возвращает декартово произведение входных итераторов. Благодаря этому мы можем избежать написания вложенных циклов во многих сценариях.
Используем модуль Itertools для написания невероятных циклов
Функция product
— лишь вершина айсберга. Изучение модуля itertools
сравнимо с открытием нового мира. В нем содержится множество полезных методов, которые могут упростить любой цикл. Полный список вы можете найти в официальной документации.
Рассмотрим несколько примеров.
Создаем бесконечный цикл
В модуле itertools
есть как минимум три метода для создания бесконечных циклов:
1. Функция count
.
natural_num = itertools.count(1) for n in natural_num: print(n) # 1,2,3,...
2. Функция cycle
.
import itertools many_yang = itertools.cycle('Hello') for y in many_yang: print(y) # 'H','e','l','l','o','H','e','l',...
3. Функция repeat
.
many_yang = itertools.repeat('Hello') for y in many_yang: print(y) # 'Hello','Hello',...
Объединяем несколько итераторов в один
Функция chain()
помогает нам объединять несколько итераторов в один.
from itertools import chain list_a = [1, 22] list_b = [7, 20] list_c = [3, 70] for i in chain(list_a, list_b, list_c): print(i) # 1,22,7,20,3,70
Выводим повторяющиеся элементы и количество их повторений
Функция groupby()
позволяет получить повторяющиеся элементы в итераторе и сгруппировать их.
from itertools import groupby for key, group in groupby('Pyttthhhonissst'): print(key, list(group)) # P ['P'] # y ['y'] # t ['t', 't', 't'] # h ['h', 'h', 'h'] # o ['o'] # n ['n'] # i ['i'] # s ['s', 's', 's'] # t ['t']
Как видите, дублирующиеся буквы сгруппированы. Более того, мы можем расширить функционал groupby()
. Например, указать, что нужно игнорировать регистр:
from itertools import groupby for key, group in groupby('PyYyYTTthHOoOnisst', lambda x: x.upper()): print(key, list(group)) # P ['P'] # Y ['y', 'Y', 'y', 'Y'] # T ['T', 'T', 't'] # H ['h', 'H'] # O ['O', 'o', 'O'] # N ['n'] # I ['i'] # S ['s', 's'] # T ['t']
Самостоятельно настраиваем цикл for
После ознакомления с примерами пора наконец разобраться, почему же циклы в Python такие гибкие и изящные. Этими своими свойствами они обязаны тому, что к итератору в цикле for
можно применять функции. Во всех примерах, приведенных выше, к итераторам применялась какая-нибудь функция. Шаблон в целом выглядит так:
for x in function(iterator)
То есть, суть itertools
проста — этот модуль применяет часто используемые функции к итератору. Если вы забыли какую-либо функцию из itertools
— реализуйте ее самостоятельно. Также стоит заметить, что все эти функции — генераторы. Вот почему с их помощью мы можем создавать бесконечные циклы.
Таким образом, написав генератор, вы можете настроить цикл for
так, как вам необходимо.
Рассмотрим простой пример:
def even_only(num): for i in num: if i % 2 == 0: yield i my_list = [1, 9, 3, 4, 2, 5] for n in even_only(my_list): print(n) # 4 # 2
Как видите, мы объявляем генератор — even_only
. Если мы встроим этот генератор в цикл, то итерация будет происходить только по четным элементам списка.
Пример выше — лишь для объяснения принципа работы. Существует множество других способов решения этой задачи — например, генераторы списков.
my_list = [1, 9, 3, 4, 2, 5] for n in (i for i in my_list if not i % 2): print(n) # 4 # 2
Вывод
Циклы в Python могут быть изящны и гибки. Такими их делают встроенные функции и генераторы.