Генераторы
Для начала нам стоит познакомиться с итераторами. Как подсказывает Wiki, итератор — это интерфейс, предоставляющий доступ к элементам коллекции (массива или контейнера). Здесь важно отметить, что итератор только предоставляет доступ, но не выполняет итерацию по ним. Это может звучать довольно запутано, так что остановимся чуть подробнее. Тему итераторов можно разбить на три части:
Итерируемый объект
Итератор
Итерация
Все эти три части связаны друг с другом. Мы обсудим их одну за одной и после перейдем к генераторам.
Итерируемый объект
Итерируемым объектом в Python называется любой объект, имеющий методы __iter__
или __getitem__
, которые возвращают итераторы или могут принимать индексы (подробности здесь). В итоге итерируемый объект это объект, который может предоставить нам итератор. Так что же представляет из себя итератор?
Итератор
Итератором в Python называется объект, который имеет метод next
(Python 2) или __next__
. Вот и все. Это итератор. Теперь об итерации.
Итерация
Если коротко - это процесс получения элементов из какого-нибудь источника, например списка. Итерация - это процесс перебора элементов объекта в цикле. Теперь, когда у нас есть общее понимание основных принципов, перейдём к генераторам.
Генераторы
Генераторы это итераторы, по которым можно итерировать только один раз. Так происходит поскольку они не хранят все свои значения в памяти, а генерируют элементы "на лету". Генераторы можно использовать с циклом for
или любой другой функцией или конструкцией, которые позволяют итерировать по объекту. В большинстве случаев генераторы создаются как функции. Тем не менее, они не возвращают значение также как функции (т.е. через return
), в генераторах для этого используется ключевое слово yield
. Вот простой пример функции-генератора:
Не самый полезный код, однако достаточно наглядный. Генераторы хорошо подходят для расчета больших наборов результатов (при использовании вложенных циклов), где вам бы не хотелось выделять память для хранения всех результатов одновременно. Многие функции из стандартной библиотеки, возвращающие списки
в Python 2, были модифицированы, для того, чтобы возвращать генераторы
в Python 3, поскольку последние требуют меньше ресурсов.
Вот пример генератора
, который считает числа Фибоначчи:
А вот так мы можем его использовать:
С помощью такого метода мы можем не волноваться об использовании большого объема ресурсов. В то же время следующая реализация алгоритма:
будет использовать огромный объем наших ресурсов при расчете достаточно больших чисел. Я уже говорил, что мы можем итерировать по генераторам только один раз, но давайте проверим это на практике. Перед этим вам надо познакомиться с одной встроенной в язык функцией - next()
. Она позволяет нам переходить к следующему элементу коллекции. Давайте проверим наше понимание:
Как видно, после прохождения по всем значениям next()
начала вызывать исключение StopIteration
. По сути, эта ошибка информирует нас о том, что все значения коллекции уже были пройдены. Может возникнуть вопрос, почему мы не получаем ошибку при использовании цикла for
. И ответ довольно прост. Цикл for
автоматически перехватывает данное исключение и перестает вызывать next
. Знали ли вы, что несколько встроенных типов данных в Python поддерживают итерирование? Давайте посмотрим:
Ок, это не то что ожидалось. Ошибка говорит, что str
не итератор. И это действительно так! Строка - итерируемый объект, но не итератор. Т.е. она поддерживает итерирование, но мы не можем делать это напрямую. Так как же нам в конечном итоге итерировать по строке? Пришло время для очередной встроенной функции - iter
. Она возвращает итератор из итерируемого объекта. int
не является итерируемым объектом, однако мы можем использовать iter
со строками!
Теперь намного лучше. Я уверен, что вас заинтересовала эта тема. Помните, что полностью изучить генераторы можно только через постоянную практику. Просто используйте генераторы везде, где это кажется удачным решением. Вы не разочаруетесь!
Last updated