Итерация (программирование)
- Итерация в программировании — в широком смысле — организация обработки данных, при которой действия повторяются многократно, не приводя при этом к вызовам самих себя (в отличие от рекурсии). В узком смысле — один шаг итерационного, циклического процесса.
Связанные понятия
В статистике метод оценки с помощью апостериорного максимума (MAP) тесно связан с методом максимального правдоподобия (ML), но дополнительно при оптимизации использует априорное распределение величины, которую оценивает.
Упоминания в литературе
Связанные понятия (продолжение)
Парсер (англ. parser; от parse – анализ, разбор) или синтаксический анализатор — часть программы, преобразующей входные данные (как правило, текст) в структурированный формат. Парсер выполняет синтаксический анализ текста.
Хеш-деревом, деревом Меркла (англ. Merkle tree) называют полное двоичное дерево, в листовые вершины которого помещены хеши от блоков данных, а внутренние вершины содержат хеши от сложения значений в дочерних вершинах. Корневой узел дерева содержит хеш от всего набора данных, то есть хеш-дерево является однонаправленной хеш-функцией. Дерево Меркла применяется для эффективного хранения транзакций в блокчейне криптовалют (например, в Bitcoin’е, Ethereum’е). Оно позволяет получить «отпечаток» всех транзакций.
В программировании, ассемблерной вставкой называют возможность компилятора встраивать низкоуровневый код, написанный на ассемблере, в программу, написанную на языке высокого уровня, например, Си или Ada. Использование ассемблерных вставок может преследовать следующие цели.
Итерация
Итерация (от лат. iteratio) – это единичное повторение того или иного действия. Этот термин является важной составляющей программирования и применяется относительно циклов, исполняющихся в процессе работы программы несколько раз. Суть: цикл выполнился единожды, значит, прошла одна итерация. Также существуют другие варианты этого определения в зависимости от контекста. Например, при организации разработки ПО по методике Agile «итерация» – это отрезок рабочего процесса длиной в 2-3 недели (в Scrum то же самое понятие называют «спринт»).
Пример, как функционирует итерация
В зависимости от своей задачи циклы могут работать по-разному. В некоторых случаях они проходят заранее заданное количество итераций и останавливаются. В других – остановка происходит по достижении определенного результата (например, верно/неверно). Число итераций тоже зависит от конкретной программы, их может быть как бесконечное количество, так и ноль.
Приведем пример, сколько итераций потребуется программе, чтобы выполнить простую функцию.
Представьте случайное число X, которое нужно умножить на 15, чтобы добиться результата, равного 200 или больше (это точка, после которой циклы перестанут повторяться). Программа будет проверять соответствие условию, умножать, снова проверять соответствие и повторять эти действия, пока не добьется заданного результата. Количество итераций напрямую зависит от того, какое значение было у X. Если это было 8, то программе потребуется 2 цикла, чтобы получить 240, что соответствует условию. При этом, если X будет равен 201, то цикл не выполнится, потому что результат уже достигнут без умножений.
Как используются итерации в программировании
Процесс разработки часто требует решения задач, в которых для достижения цели необходимо повторить одно и то же действие несколько раз. Это могут быть разные вещи: получение информации, вывод сообщения, отправка оповещения и пр. В программировании работает важный принцип DRY, который расшифровывается так don’t repeat yourself – «не повторяйся». В соответствии с этим «законом» не стоит вручную выполнять все описанные выше действия. Поэтому их лучше делегировать написанному разработчиком циклу, который сделает необходимое число итераций вплоть до достижения результата.
С помощью итераций можно контролировать то, как программа будет выполнять поставленную перед ней задачу. На основе этого выделяют два вида повторений.
- Контролируемые подсчетом. Разработчик может заставить цикл исполниться, например, 10 раз подряд – именно это будет условием, чтобы перестали запускаться последовательные итерации. Как правило, такие итерации применяют для ввода операторов for. В этом случае разработчик обозначает стартовую точку итерации и задает число повторений.
- Условные. Разработчик может установить условием выполнения не количество повторений, а некий конкретный итог. Как в примере выше, это может быть достижение определенного числа или получение информации от конкретного источника. Программа будет проверять исполнение условия в начале или по окончании каждой итерации (зависит от того, как задаст программист). Если результат будет достигнут, цикл прекратится. При этом специалисту не придется делать что-либо вручную. Такой подход практикуется при машинном обучении.
Итерации встречаются практически во всех распространенных языках программирования. Ими пользуются программисты на C++, PHP, JavaScript, Python и многих других.
Специфика создания и работы итераций
Разработчик создает сложные и простые итерации, чтобы добиться определенной цели. Для этого он выполняет следующие действия и проверяет работу получившегося кода.
Объявление цикла. Программист описывает условия для выполнения цикла перед тем, как объявить его, то есть указывает, в каком случае работа будет считаться выполненной. Это может быть число итераций или другие события. Этот процесс – объявление цикла с предусловием.
Цикл с предусловием выглядит примерно так:
В некоторых случаях исполнение программы сразу запускает цикл и сверяется со своей целью только после завершения итерации – это цикл с постусловием.
Схема цикла с постусловием:
Написание тела цикла. После объявления программист должен написать тело – то, что непосредственно будет делать цикл, какие именно итерации будут выполняться. Это подпрограмма, поэтому для ее отделения от основного кода используются <> или слова begin и end. Если программист использует Python, то для этих целей он применяет отступы.
Работа итераций. В своей работе программа может дойти до момента, когда нужно выполнить цикл и все его итерации. Сначала она считывает строку с объявлением. Затем проверяются условия выхода из цикла, если их нет, запускается тело. Каждая итерация предполагает единоразовое исполнение цикла. Программа будет сверяться с нужным результатом и повторять действия из тела цикла столько раз, сколько потребуется.
Разные варианты входа в цикл. Существуют разные варианты того, как будет работать подпрограмма в рамках основного кода. Условия могут проверяться до входа в тело, еще на этапе объявления цикла. Тогда ничего не запускается. В противном случае программа сразу входит в тело цикла и проверяет, соответствует ли выполнение кода условию, только после первой итерации. Получается, если результат соответствует условиям, цикл выполнится либо 0 раз, либо 1 раз.
Что означает «итерируемый объект»
Итерируемые объекты – те, чье содержимое поддается пересчету. Наиболее примитивный пример итерируемого объекта – строка, потому что в рамках цикла ее возможно пройти от начала до конца побуквенно (количество символов в ней измеримо, и у каждого из них есть строго заданное место).
Чуть более сложный пример подобных объектов – массив. Он представляет собой переменную, внутри которой перечислены значения, имеющие порядковый номер. Поскольку структура массива сходна с числовым рядом, ее тоже можно пересчитать. Для этого можно двигаться в направлении от первого объекта к последнему и наоборот.
Итерируемые объекты тесно связаны с понятием итерируемости, которое встречается во многих современных языках программирования (например, JavaScript). Необходимость обрабатывать подобные объекты часто возникает при парсинге, анализе строк или прочих данных. Понятие итерируемости важно с точки зрения того, можно ли обработать объект, изучить его содержимое.
Итерация и итератор – не перепутайте
Итератор – это похожее по звучанию понятие, которое представляет собой переменную-счетчик. Она предназначена для записи количества итераций цикла. Как правило, итераторы прописывают для циклов, которые должны выполняться определенное количество раз.
Принцип работы итератора. Работу счетчиков в их простом варианте можно описать так: значение увеличивается на единицу, как только заканчивается каждая итерация. Цикл завершится в момент, когда в итераторе будет достигнут определенный порог (целевое число повторений). Программа начинает отсчитывать с 0, но иногда – с 1. В теле цикла прописывается значение итератора, которое можно в процессе выполнения подпрограммы сравнивать с какими-либо другими показателями или прибавлять к ним. При этом изменение самого итератора обычно приводит к тому, что цикл перестает работать, итерации перестают повторяться.
Изменение функционирования алгоритма. Многие языки программирования позволяют задавать итератору другое поведение. Это значит, что алгоритм будет прибавлять к значению не 1, а, например, 2 или отнимать 1 при каждом завершении итерации вплоть до достижения 0. Необходимость в таком изменении встречается редко, обычно к нему не прибегают без острой необходимости.
Как может работать итерация при проходах по объекту
Многие языки программирования включают в себя специальные форматы циклов, которые предназначены для обработки массивов и прочих итерируемых объектов.
Подобные циклы отличаются от стандартных тем, что итератор в них работает иначе: в процессе прохода он последовательно (от первого к последнему) принимает каждое значение из содержимого объекта. На практике это работает таким образом, как описано в примере ниже.
Пример: цикл предполагает проход по строке со словом «тетрадь». Итераций в итоге будет 7. Сначала итератор примет значение «т», затем «е» и так далее вплоть до «ь». Как только строка закончится, цикл завершится.
Подобный формат работы итератора удобен, когда нужно взаимодействовать с отдельными элементами, находящимися в более крупных единицах. В массиве может быть большое количество строк, каждую из которых нужно обработать отдельно. Именно для решения таких задач используется проход в рамках цикла. Обратите внимание, что возможность так делать есть только при работе с итерируемыми объектами.
В чем преимущества итераций при разработке
Повторения в программировании – распространенное явление, которое пользуется популярностью по нескольким причинам.
Итерации используют мало памяти
По сравнению с остальными структурами программирования, для реализации повторения нужно меньше памяти. В рекурсивном программировании применяются структуры стека, которые предполагают, что для ввода код каждый раз будет вызывать переменные входные данные. Получается, что для каждого возвращения к входному стеку программе понадобится очень много оперативной памяти.
В то же время итерация применяет структуру очереди, в которой содержатся сценарии кодирования для входа и выхода из цикла. Программа не требует затратных операций, в ней отсутствуют постоянные вызовы к служебному стеку кода, как следствие, нужны меньшие объемы памяти для корректной работы.
Итерации облегчают оптимизацию
Благодаря повторениям удается оптимизировать работу облачных сервисов и онлайн-приложений. Это позволяет улучшить пользовательский опыт и ускорить загрузку. Например, итерации используются, когда нужно автоматически заполнить платежные данные покупателя при покупке из-под авторизованной учетной записи.
Итерации отвечают за автоматизацию
Разработчики могут упростить свою работу за счет внедрения итераций в участки, где не нужно участие человека. Например, нередко в бизнес-сфере и при создании ПО их применяют для поиска данных в различных базах.
Примеры задач, которые решаются итеративно
Существует набор задач, которые традиционно решаются с применением итераций. Перечислим некоторые из них.
Циклический подсчет. Это может быть операция с математическими выражениями. Так, итерации помогают вычислять факториал или конкретное число Фибоначчи. Для решения подобных задач подходит и рекурсивный метод, но он требует несравнимо больше ресурсов.
Перебор. Для этого примера рассмотрим упрощенную схему: сервер предоставил объемный массив строк, каждая из которых представляет информацию о продукте с сайта интернет-магазина. Для выполнения некой задачи нужно найти в этих строках определенный повторяющийся элемент и перенести его в другое место. Чтобы достичь этой цели, можно использовать итеративный алгоритм, для этого есть все необходимые условия: массив – итерируемый объект, все его элементы пронумерованы, обойти их можно с помощью цикла.
Подсчет времени до выполнения условия. Простой пример: имеется статистика по приросту пользователей на сайте (A), нужно выяснить, через какой промежуток времени аудитория достигнет определенного значения (B) при таких же темпах увеличения. В этом случае разработчик не знает, сколько итераций потребуется, поэтому пишет цикл с незаданным числом повторений. Он выполняется раз за разом, пока верно или неверно условие (достигнуто или не достигнуто значение B), в это же время итератор с каждым шагом увеличивается на 1, отражая месяцы. Останется написать код так, чтобы программа после остановки цикла выдавала показатель счетчика – это и будет требуемый промежуток времени.
В чем отличия итерации от рекурсии
Итерация не является единственным повторяющимся алгоритмом, кроме нее существует, например, рекурсия, которая работает по другому принципу. Два принципиальных различия между ними:
- подпрограмма в итеративном подходе вызывается, завершается и вызывается снова несколько раз, при этом запускается она извне;
- подпрограмма в рекурсивном подходе вызывает сама себя и позволяет таким образом запускать сразу несколько вложенных друг в друга подпрограмм.
В рекурсии, когда одни аргументы отвечают искомым параметрам, функция отдает значение в предыдущий экземпляр подпрограммы и закрывается. Постепенно все процессы завершаются от внутренних к внешним, но такой подход требует большого количества памяти. На этом фоне итерации выглядят более предпочтительным вариантом.
Итерация
Итерация – это одно повторение чего-либо. В программировании термин используют применительно к циклам: подпрограммам, которые выполняются несколько раз. Одно выполнение называется одной итерацией.
Освойте профессию «Frontend-разработчик»
Циклы бывают разными. Некоторые выполняются, пока верно или неверно определенное условие. Другие отрабатывают заданное количество раз. Соответственно, и итераций может быть разное количество. Встречаются циклы с бесконечным количеством итераций или такие, которые не выполнятся ни разу – там итераций будет ноль.
Например, цикл должен умножать число a на 10, пока оно не станет больше или равно 100. Если начальное значение a будет равно 10 – значит, цикл выполнится один раз: 10 * 10 = 100. Получится одна итерация. А вот если a = 0,1, то цикл выполнится уже трижды: 0,1 * 10 * 10 * 10 = 100. Выйдет три итерации.
Если же a изначально будет равно, скажем, 101, цикл не выполнится ни разу – ноль итераций.
Создавайте интерфейсы сервисов, которыми пользуются все
Для чего нужны итерации
В разработке часто встречаются задачи, когда одно и то же действие нужно повторить несколько раз: вывести сообщение, получить или записать данные, отправить запрос и так далее. Делать это вручную неудобно и нарушает важный принцип программирования – DRY, don’t repeat yourself, или «не повторяйся». Вместо разработчика действие повторяет цикл – совершает нужное количество итераций.
В более узком смысле итерации помогают контролировать выполнение программы.
- Например, если сказать циклу «выполнись 10 раз», то он выполнится ровно столько раз – в этом можно быть уверенным.
- И наоборот: если мы сами не знаем, сколько итераций нужно, цикл будет проверять условие для завершения на каждой из них. Когда заданное разработчиком условие выполнится – он закончится. Так что считать итерации вручную не понадобится.
Поэтому итерации и циклы в целом – одно из базовых понятий в программировании. Они есть практически во всех популярных языках: Java, JavaScript, PHP, Python, C++ и так далее.
Как устроены итерации
Структура цикла. Когда программист объявляет цикл, он сначала описывает его условия – инструкции для выполнения. Например, он пишет условие для выхода из цикла или количество повторений.
После объявления пишется тело цикла. Это подпрограмма, которая обычно отделена от основного кода фигурными скобками или ключевыми словами begin и end. В Python для этого используются отступы.
Выполнение итерации. Когда программа доходит до цикла, она смотрит на объявление. Если условий для выхода из цикла нет, то начинает исполняться код внутри цикла – его тело. Этот внутренний код пишет разработчик, он может быть любым.
В ходе каждой итерации тело цикла выполняется один раз. Потом программа снова проверяет условие – и так далее.
Другой вариант поведения. Описанное выше поведение можно менять. Есть разные виды циклов: один проверяет условие перед входом в подпрограмму, а другой сразу заходит в тело цикла. Условие он впервые проверяет уже после первой итерации.
Соответственно, если условие изначально неверно (например, «повторять, пока 0 равен 1»), первый тип цикла выполнится ноль раз, а второй – один раз. Так происходит, потому что второй тип не проверяет условие перед первой итерацией.
Итерация и итератор
Итерацию не нужно путать с другим похожим понятием – итератором. Так называется переменная-счетчик: в нее программа записывает, сколько раз цикл уже выполнился. Обычно итераторы используют в циклах, которые должны отработать определенное количество раз.
Как работает. Простейший итератор работает так: в конце каждой итерации к нему прибавляется единица. Как только значение итератора достигнет какого-то заданного порога, цикл остановится. Счет обычно начинается с нуля. Реже – с единицы.
Значение итератора доступно в теле цикла. Можно сравнивать его с чем-то другим или прибавлять к другой переменной. А вот сам итератор в теле цикла менять не стоит: цикл сломается. Некоторые языки это допускают, но делать так не нужно.
Изменение поведения. На самом деле в большинстве языков программирования можно менять поведение итератора. Например, прибавлять к нему не единицу, а другое число. Или даже наоборот: отнимать от числа-оператора единицу, пока он не станет равным 0. Но нужда в подобном встречается редко, а без острой необходимости так лучше не делать – это ухудшает читаемость кода.
Исключение – проход по объекту, о котором мы расскажем ниже. Там поведение итератора отличается от обычного счетчика от 0 до n.
Интерфейс итератора. Иногда можно столкнуться с понятиями «интерфейс Iterator», «протокол Iterator» и похожими. Это не совсем то же самое. Если говорить простыми словами, это объект, который умеет заниматься перечислением. Он встроен в некоторые типы данных. Но с ним можно столкнуться уже на более продвинутых уровнях изучения разработки: без знания основ сложно понять, что это такое и как работает.
Также в некоторых стандартных функциях языков есть встроенные итераторы. Они тоже работают как счетчики, но цикл разработчику писать не приходится.
Что означает «итерируемый объект»
Еще одно понятие из программирования – итерируемость. Некоторые сущности называют итерируемыми, или iterable. Если вы будете изучать какой-либо из современных языков, например, JavaScript, то с большой вероятностью столкнетесь с этим понятием.
Итерируемые объекты – это такие, содержимое которых можно пересчитать. Простейший пример – строка: в цикле можно пройти от первой ее буквы к последней. У нее конечное количество символов, и у каждого из них есть строго заданное место.
Второй пример итерируемого объекта – массив. Это переменная, в которую записано несколько значений, и у каждой есть свой порядковый номер. Структура похожа на числовой ряд. Массив тоже можно пересчитать: двигаться в цикле от первого объекта к последнему или наоборот.
Необходимость проходить по таким объектам часто встречается в разработке, например, при парсинге, обработке строк или других данных. Поэтому итерируемость – важное понятие.
Интерфейс итератора, о котором мы упомянули выше, в обязательном порядке есть у итерируемых объектов. Это важное условие. Без него объект не получится пересчитать.
Станьте Frontend-разработчиком
и создавайте интерфейсы сервисов, которыми пользуются все
Итерация в проходах по объекту
Во многих языках программирования есть специальные форматы циклов для прохода по массивам и другим итерируемым объектам. По сути это те же циклы с заданным количеством повторений, просто с другим итератором.
Итератор в таких циклах по очереди принимает каждое значение из содержимого объекта – от первого к последнему. Разберем на примере.
Допустим, в цикле нужно пройти по строке «школа». Повторений будет пять. Итератор будет по очереди принимать значения «ш», «к», «о», «л», «а». После этого он дойдет до конца строки, и цикл завершится.
Такой формат удобен, потому что при работе с массивом или строкой часто бывает нужно работать с отдельными элементами из набора. Например, массив содержит множество сообщений, и каждое нужно обработать отдельно от другого. Это удобно сделать с помощью прохода по массиву в цикле.
Проходить таким образом можно только по итерируемым объектам.
Чем итерации отличаются от рекурсии
Еще один пример повторяющегося алгоритма – рекурсия. Но она работает по другому принципу. Рекурсивный и итеративный подход различаются следующим:
- итеративная подпрограмма вызывается, завершается и снова вызывается несколько раз подряд;
- рекурсивная функция вызывает сама себя, но с другими аргументами. В итоге одновременно может быть открыто несколько вложенных друг в друга подпрограмм.
У рекурсивных функций тоже есть условия для выхода, но пишутся они иначе. Если аргументы отвечают какому-то условию, функция возвращает некоторое значение и закрывается. Это значение передается в предыдущий экземпляр – и так далее. Они завершаются от внутренних к внешним.
В решениях некоторых задач рекурсия выглядит изящнее, чем итерации, но такой подход отнимает довольно много памяти. Поэтому использовать его не всегда оптимально – чаще его рекомендуют заменять на итеративный.
Примеры задач, которые решаются итеративно
Приведем несколько примеров задач, которые удобно решать с помощью нескольких итераций с условием или заданным количеством повторений. Это далеко не исчерпывающий список, но перечислить все возможные задачи практически невозможно. Их существует слишком много.
Циклический подсчет. Простой пример – расчет какого-нибудь математического выражения, например, факториала или n-го числа Фибоначчи. Кстати, такие задачи можно решать и рекурсивно, но часто это неоптимально с точки зрения использования памяти.
Факториал – это число, которое получается, если по очереди умножить друг на друга числа от 1 до n. Например, факториал из 5 (записывается как 5!) равен 1 * 2 * 3 * 4 * 5 = 120.
Так как умножать числа нужно несколько раз и по очереди, здесь будет уместен цикл с заданным количеством итераций. Итератор будет отсчитывать значения от 1 до n.
Кстати, итератор можно использовать при перемножении – хранить в переменной промежуточный результат и на каждом шаге умножать на итератор. Ведь он точно так же по очереди меняется от 1 до n. Правда, тогда надо сразу прописать, чтобы отсчет итератора начался с 1, а не с 0, как обычно. Иначе весь результат окажется равен 0.
Перебор массива или объекта. Представьте, что с сервера пришел большой массив строк. В каждой из них – данные по какому-то товару с сайта. Это условный упрощенный пример, на самом деле особенности передачи данных с сервера несколько сложнее.
Нужно обработать этот массив строк: выделить из каждой строки отдельные части и записать их в нужные места. Получается итеративный алгоритм. Элементы массива пронумерованы, и это итерируемый объект, поэтому его можно обойти в специальном цикле для прохода.
На каждом шаге цикла можно выполнять заданные действия с нужным элементом. На следующем шаге те же действия выполнятся с другим элементом – и так далее до конца массива. Не нужно несколько раз писать одно и то же, все сделает программа.
Правда, в некоторых языках для такой обработки массива существуют специальные функции. С их помощью пройти и обработать итерируемый объект можно в одну строку – но такое есть не везде, и это более продвинутый уровень.
Повтор до выполнения условия. Абстрактный пример: каждый месяц количество пользователей сайта увеличивается в среднем на n процентов. Нужно проанализировать, сколько месяцев пройдет, прежде чем количество пользователей достигнет определенного числа m. Будем считать, что динамика прироста остается одинаковой.
В этом случае мы точно не знаем, сколько повторений нужно. Но задача сводится к тому, чтобы несколько раз высчитать n % от текущего количества, прибавить к текущему количеству и сравнить с пороговым числом m. На каждом шаге счетчик нужно увеличивать на 1 – один месяц. Это можно сделать итеративно.
Понадобится цикл с неопределенным количеством повторений – тот, который выполняется, пока верно или неверно какое-либо условие. Можно написать условие «повторять, пока количество пользователей не станет равно или больше m» – и программа сама повторится нужное количество раз.
Потом подпрограмма должна вернуть количество месяцев, то есть фактическое количество итераций. Кстати, цикл можно использовать повторно с разными n и m – для каждых входных данных цикл подсчитает свой результат. Соответственно, будет меняться и количество итераций.
Как часто нужно решать задачи итеративно
Решать задачи в цикле – стандартный алгоритм для многих языков. Но в коммерческой разработке этот подход не всегда применим. Например, в JavaScript считается, что лучше обходиться без перебора массивов в цикле – вместо этого советуют применять специальные встроенные функции. Но в целом итерации встречаются много где, а конкретный подход во многом зависит от языка программирования.
С итерациями человек сталкивается еще в самом начале обучения программированию. Циклы – часть базового синтаксиса практически любого языка. А без нескольких итераций цикл не получится.
Даже если впоследствии программист в основном будет работать с языком, где редко нужны циклы, знать их все равно надо. Это основа, база, которая помогает понять структуру и принцип работы с языком.
Научитесь создавать удобные и эффектные сайты, сервисы и приложения, которые нужны всем. Сегодня профессия на пике актуальности: в России 9000+ вакансий, где требуется знание JavaScript.
Просто о сложном — итераторы в Python
Итератор, итерации — всё это в голове вызывает ассоциации с массивами и с циклическими операциями. В данной статье мы разберемся с вопросом, что такое итератор, чем он отличается от итерируемого объекта, как работает цикл for “под капотом” и, наконец, как реализовать свой собственный итератор.
Важно понимать, что итератор и итерируемое — понятия хоть и созвучные, но абсолютно разные. Начнём с итерируемого. Итерируемый объект — это объект, предоставляющий возможность поочередного прохода по своим элементам. Под итерируемым объектом понимают: списки, строки, кортежи, словари, файлы. Если говорить строго, то объект является итерируемым, если из него можно получить итератор.
Итератор — это то, что можно получить из итерируемого объекта. Мы можем сказать, что итератор — это тип, позволяющий реализовать поток данных и предлагающий средства для продвижения по нему. В Python итератором является любой объект, реализующий так называемый протокол итераторов, который должен содержать в себе два метода: __iter()__ и __next()__. Метод __iter()__ должен возвращать итератор. Метод __next()__ — следующий элемент потока данных.
Давайте рассмотрим небольшой пример. Предположим, у нас есть список arr из пяти элементов.
Как мы знаем, список — это итерируемый объект. Из любого итерируемого объекта можно получить итератор. Давайте “извлечем” итератор из списка arr.
Также мы знаем, что для реализации протокола итератора, объект должен реализоваться метод __next__(), попробуем его вызвать.
Как мы видим, вывелся первый элемент списка. Не трудно догадаться, что будет, при повторных вызовах метода __next__().
Как мы видим, итератор в лице метода __next__() предоставил нам инструмент для прохода по потоку данных, в нашем случае — по всем элементам списка, а дойдя до конца — вызвал специальное исключение StopIteration, сигнализирующее о том, что итератор исчерпал все доступные ему значения.
Данная конструкция для вывода элементов списка, конечно, очень объемна. Обычно для этой задачи мы используем цикл for.
А как for перебирает элементы? На самом деле, “под капотом”, цикл for — это синтаксический сахар для цикла while. А цикл while как раз-таки работает ни с чем иным как с итератором итерируемого объекта. Давайте посмотрим на то, как отрабатывает цикл for без прикрас:
Как мы видим, white делает ровно то, что мы сделали руками выше. Извлекает итератор, и пока есть данные выводит каждый элемент. По исчерпанию данных поднимается исключение StopIteration, который в нашем случае и заканчивает white, выведя информацию о нем в консоль, потому что в отличие от for-а, while не обрабатывает данное исключение.
При желании, мы можем записать наш код немного красивее, не прибегая к непосредственному упоминанию методов __iter__() и __next__(), а заменив их на методы-обертки iter() и next().
Ничего не мешает нам самим написать класс реализующий итератор, объект которого может быть передан цикл for для работы с потоком данных, содержащемся в объекте. Предположим мы хотим написать функционал, который бы возвращал квадрат чисел от текущего введенного и до одного. Давайте сделаем это с помощью итераторов.
Попробуем создать объект нашего класса и пройтись по нему циклом for:
А теперь тоже самое, но используя while и явную работу с итератором.
Как мы видим, наш итератор прекрасно себя чувствует и работает как полагается. В нашем случае объект нашего класса и есть итератор сам по себе, поэтому __iter__() возвращает self.
Таким образом, мы разобрались, что такое итерируемые объекты, что такое итераторы, как на самом деле происходит проход по итерируемому объекту и как создать свой итератор. Итераторы очень полезны в контексте генераторов, но об этом мы поговорим в следующей статье.