Лутц М. - Изучаем Python (1077325), страница 99
Текст из файла (страница 99)
Вследствие этого часто они представляют собой удобную альтернативу вычислению всей серии значений заранее и ручному сохранению и восстановлению состояния в классах. Функции-генераторы при приостановке автоматически сохраняют информацию о своем состоянии, под которым понимается вся локальная область видимости, со всеми локальными переменными, которая становится доступной сразу же, как только функция возобновляет работу. Главное отличие функций-генераторов от обычных функций состоит в том, что генератор поставляет значение, а не возвращает его — инструкция уте10 приостанавливает работу функции и передает значение вызывающей программе, при этом сохраняется информация о состоянии, необходимая, чтобы возобновить работу с того места, где она бы- 461 2ще раз об итераторах: генераторы ла приостановлена. Это позволяет функциям воспроизводить последовательности значений в течение долгого времени, вместо того чтобы создавать всю последовательность сразу и возвращать ее в виде некоторой конструкции, такой как список.
Функции-генераторы тесно связаны с понятием протокола итераций в языке Ру()топ. Проще говоря, функции, содержащие инструкцию уте1б, компилируются особым образом, как генераторы — при вызове они возвращают объект-генератор, поддерживающий интерфейс итераций. Функции-генераторы могут также содержать инструкцию гетзгп, которая завершает генерацию значений. Объекты итераторов в свою очередь определяют метод пехт, который либо возвращает следующий элемент в итерации, либо возбуждает исключение (Ятор1тегат(сп) в конце итераций.
Доступ к итератору можно получить с помощью встроенной функции (те г. Циклы гог в языке Ру- 1)топ используют такой итерационный протокол, если он поддерживается, для обхода последовательностей (или генераторов последовательностей). Если протокол не поддерживается, инструкция г'сг терпит неудачу и возвращается к операции индексирования последовательности. Пример функции-генератора Генераторы и итераторы — это достаточно сложные особенности языка, поэтому обязательно загляните в руководства по стандартной библиотеке языка Ру()топ, где найдете исчерпывающую информацию. Чтобы проиллюстрировать основные моменты, рассмотрим следующий фрагмент, где определяется функция-генератор, которая может использоваться для генерации серии квадратов чисел »> Еет депепоагее(З)'.
тот 1 1п галсе(И): у1е1б 1 ** 2 а Позднее лродолиити работу с этого места Эта функция поставляет значение и тем самым возвращает управление вызывающей программе на каждой итерации цикла — когда она возобновляет работу, восстанавливается ее предыдущее состояние и управление передается непосредственно в точку, находящуюся сразу же за инструкцией у1е1б. Например, при использовании в заголовке цикла гог управление возвращается функции на каждой итерации в точку, находящуюся сразу же за инструкцией у1е1б: »> аког 1 1п депепоегеа(5); а Возобноеити работу аункции Генераторы появились в языке Русаков, начиная с версии 2.2.
В версии 2.2 для нх использования необходимо было применять специальную инструкцию тпрогт: тотиге щрогт еепегатога (подробнее об атой форме инструкции рассказывается з главе 18). Генераторы стали доступны еще з версии 2.2 зо многом благодаря тому, что лежащий е нх основе протокол не требовал нового ключевого слова у1е!О, нарушающего обратную совместимость. 462 Глава 17. Расширенные возможности функций П Вивости последнее полученное значение ргспС 1, 0.1:4:9:16 »> Для завершения генерации значений функция может либо воспользоваться инструкцией гесогп без значения, либо просто позволить потоку управления достичь конца функции. Если вам интересно узнать, что происходит внутри цикла Тог, вызовите функцию-генератор напрямую: »> х = двпвдьагвв(4) »> х <депвга1ог оЬ)вс1 а1 Ох00860378> Здесь обратно был получен объект-генератор, который поддерживает протокол итераций (т.
е. имеет метод пех1, который запускает функцию или возобновляет ее работу с места, откуда было поставлено последнее значение, а также возбуждает исключение 31ор11егатссп ПО достижении конца последовательности значений): »> х.пвхс() 0 »> х,пвхсО 1 »> х,пвхс() 4 »> х.пвхс() 9 »> х.пвхс() ТгасеЬаск (аов1 гвсеп1 са11 1ав1); Рс1е "<рувпв114453>", 11пе 1, 1п <аобо1е> х.пвхс() Бсор11вгассоп Циклы Гог работают с генераторами точно так же — вызывают метод пвхс в цикле, пока не будет перехвачено исключение. Если итерируемый объект не поддерживает этот протокол, вместо него цикл Гог использует протокол доступа к элементам по индексам. Обратите внимание, что в этом примере мы могли бы просто сразу создать список всех значений: »> бвс ЬЬ11бвдьагвв(п): гвв = () Гог 1 сп галде(п); гвв.аррвпб(1 ° ° 2) ГВЬЬГЬ Гвв »> Гог х 1п Ьь11бвдьагвв(5): рг1п1 х, 0 .
'1: 4; 9: 16 4бЗ [ще раз об итераторах: генераторы В такой ситуации мы могли бы использовать любой из приемов: цикл бог, функцию зар или генератор списков: »> Гог х 1п [и ° 2 Гог и 1п гапое(6)): рг1пт х, О; 1; 4: 9; 16 »> Гог х 1п эар((1еэоса х.х* ° 2), гапое(б)); рг1пт х, О. 1. 4.
9. 16 Однако генераторы дают возможность избежать необходимости выполнять всю работу сразу, что особенно удобно, когда список результатов имеет значительный объем или когда вычисление каждого значения занимает продолжительное время. Генераторы распределяют время, необходимое на создание всей последовательности значений, по отдельным итерациям цикла. Кроме того, в более сложных случаях использования они обеспечивают простую альтернативу сохранению состояния вручную между вызовами в объектах классов (подробнее о классах рассказывается в шестой части книги) — в случае с генераторами переменные функций сохраняются и восстанавливаются автоматически. Расширенный протокол функций-генераторов: беппо и пехт В версии Ру1Ьоп 2.5 в протокол функций-генераторов был добавлен метод бепо.
Метод аепа не только перемещается к следующему элементу в последовательности результатов, как это делает метод пехт, но еще и обеспечивает для вызывающей программы способ взаимодействовать с генератором, влияя на его работу. С технической точки зрения уте1С в настоящее время является не инструкцией, а выражением, которое возвращает элемент, передаваемый методу аепб (несмотря на то, что его можно использовать любым из двух способов, — как у1е1С Х или как А = уте16( Х) ). Значения передаются генератору вызовом метода запс(оа1оа).
После этого программный код генератора возобновляет работу, и выражение уте1С возвращает значение, полученное от метода эапс. Когда вызывается обычный метод пехт( ), выражение ута1О возвращает (топе. Метод запб может использоваться, например, чтобы реализовать генератор, который можно будет завершать из вызывающей программы. Кроме того, генераторы в версии 2.б поддерживают метод (и гоп(туре) для возбуждения исключения внутри генератора в последнем выражении у!е10 и метод с1обе(), который возбуждает исключение ОепегатсгЕх(т внутри генератора, чтобы вынудить его завершить итерации. Мы не будем углубляться здесь в эти расширенные возможности — за дополнительной информацией обращайтесь к стандартным руководствам по языку Ру1)топ. 464 Глава 17.
Расширенные возможности функций Итераторы и встроенные типы Как мы видели в главе 13, встроенные типы данных спроектированы так, чтобы воспроизводить объекты итераторов в ответ на вызов встроенной функции 1(ег. Итераторы словарей, например, во время итераций воспроизводят список ключей: >» 0 = ('а':1, 'Ь';2, 'с';3) »> х = нег(0) »> х.пехс() 'а' »> х.пехс() 'с' Кроме того, все разновидности итераций (включая циклы тог, функцию эар, генераторы списков и других, с которыми мы встречались в главе 13) в свою очередь спроектированы так, чтобы для определения — поддерживается ли протокол автоматически вызывать встроенную функцию 1(ег.
Именно поэтому существует возможность выполнить обход ключей словаря, не прибегая к вызову метода Кеуа, строк в файле — без вызова метода геа011пеэ или х геаэ)[пеа и т. д.: »> Гсг Кеу 1п 0; рг1пс Кеу, 0[Кеу) а з с 3 Ь 2 Мы также видели,что при использовании итераторов файлов интерпретатор Ру()зоп просто загружает строки из файла по мере необходимости: »> тсг 11пе 1п преп('сеер.тхт'): рг1п1 11пе, тг а ьст а т)еап нсипс.