English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
В этой статье вы узнаете, как с помощью генераторов Python легко создавать итерации, в чем разница между ними и обычными функциями, и почему их стоит использовать.
С помощью PythonКонструкцияИтераторЕсть много издержек; нам нужно реализовать класс, используя методы __iter__() и __next__(), чтобы отслеживать внутренние состояния, вызывать StopIteration при отсутствии возвращаемых значений и т.д.
Это и длинно, и противоречиво. В этом случае генераторы могут быть полезны.
Генераторы Python - это простой способ создания итераторов. Все вышеупомянутые издержки автоматически обрабатываются генераторами Python.
Короче говоря, генератор - это функция, которая возвращает объект (итератор), который мы можем итерировать (по одному значению за раз).
Создание генератора в Python очень просто. Как и определение обычной функции с использованием оператора yield вместо оператора return.
Если функция содержит по крайней мере один оператор yield (он может содержать другие операторы yield или return), то она становится генераторной функцией. Операторы yield и return оба возвращают некоторые значения из функции.
Различие заключается в том, что когда оператор return полностью завершает функцию, оператор yield приостанавливает функцию, сохраняя все свои состояния, и продолжает выполнять их при последующих вызовах.
Это различие между генераторной функцией иОбычные функцииРазличия.
Функция генератора содержит один или несколько операторов yield.
При вызове он возвращает объект (итератор), но не начинает выполнять его немедленно.
Методы, такие как __iter__() и __next__(), реализуются автоматически. Таким образом, мы можем использовать next() для遍ения объектов.
Как только функция генерирует результат, функция останавливается, и контроль передается вызователю.
Локальные переменные и их состояние запомнились между последовательными вызовами.
В конце, при завершении функции автоматически вызывается StopIteration при дальнейших вызовах.
Это пример, который объясняет все вышеупомянутые моменты. У нас есть функция my_gen(), названная несколькими语句ами yield, как генератор функции.
# Простой генераторный функционал def my_gen(): n = 1 print('Это первый вывод') # Генераторная функция содержит оператор yield yield n n += 1 print('Это второй вывод') yield n n += 1 print('Это последний вывод') yield n
Объяснение интерактивного выполнения приведено ниже. Запустите эти команды в Python Shell, чтобы увидеть вывод.
>>> # Он возвращает объект, но не начинает выполнять его немедленно. >>> a = my_gen() >>> # Мы можем использовать next() для遍历这些项. > Это первый вывод 1 >>> # Как только функция генерирует результат, функция останавливается, и контроль передается вызовителю. >>> # Локальные переменные и их состояние запомнились между последовательными вызовами. > Это второй вывод 2 > Это последний вывод 3 >>> # В конце, при завершении функции автоматически вызывается StopIteration при дальнейших вызовах. > >>> next(a) Traceback (последний вызов в конце): ... > >>> next(a) Traceback (последний вызов в конце): ...
StopIterationВажно отметить одну интересную вещь в примере выше: переменная запоминается между вызовами.worth.
В отличие от обычных функций, локальные переменные не разрушаются при создании функции. Кроме того, объект генератора можно итерировать только один раз.
Чтобы возобновить процесс, нам нужно использовать что-то вроде = my_gen() для создания другого объекта генератора.
Внимание:Важно отметить, что мы можем напрямую использовать генератор сЦикл forВместе.
Это потому, что цикл for принимает итератор и итерирует его с помощью функции next(). При возникновении StopIteration он автоматически завершается.Понять, как реализовать цикл for на практике в Python.
# Простой генераторный функционал def my_gen(): n = 1 print('Это первый вывод') # Генераторная функция содержит оператор yield yield n n += 1 print('Это второй вывод') yield n n += 1 print('Это последний вывод') yield n # Использование цикла for for item in my_gen(): print(item)
При запуске программы вывод будет следующим:
Это первый вывод 1 Это второй вывод 2 Это последний вывод 3
Пример, приведенный выше, не очень полезен, мы рассмотрели его только для понимания того, что происходит в фоновом режиме.
Обычно генераторные функции реализуются с помощью циклов с надлежащими условиями终止а.
Давайте рассмотрим пример генератора для переворота строки.
def rev_str(my_str): length = len(my_str) for i in range(length - 1, -1, -1): yield my_str[i] # Цикл for для переворота строки # Вывод: # o # l # l # e # h for char in rev_str("hello): print(char)
В этом примере мы используем функцию range() в цикле for для получения индексов в обратном порядке.
Доказано, что этот генераторный функционал подходит не только для строк, но и для других типов итерируемых объектов, таких какlist,tupleи т.д.
Использование генераторных выражений позволяет легко динамически создавать простые генераторы. Это упрощает создание генераторов.
Создание функции lambdaАнонимным функциям, генераторные выражения создают анонимные генераторные функции.
Генераторные выражения имеют грамматическую структуру, аналогичнуюPythonвПонимание спискаГрамматика. Но вместо квадратных скобок используйте круглые.
Основное различие между списковыми пониманиями и генераторными выражениями заключается в том, что списковые понимания создают весь список, в то время как генераторные выражения создают один элемент за раз.
Они несколько ленивы и создают элементы только по мере необходимости. Поэтому генераторные выражения значительно эффективнее по памяти по сравнению с эквивалентными списковыми пониманиями.
# Инициализация списка my_list = [1, 3, 6, 10] # Использование списка для вычисления квадратов каждого элемента # Вывод: [1, 9, 36, 100] [x**2 for x in my_list] # То же можно сделать с помощью генераторного выражения # Вывод: <generator object <genexpr> at 0x0000000002EBDAF8> (x**2 for x in my_list)
Как мы видим, генераторные выражения не сразу производят необходимый результат. Вместо этого они возвращают объект генератора, который производит элементы по мере необходимости.
# Инициализация списка my_list = [1, 3, 6, 10] a = (x**2 for x in my_list) # Вывод: 1 print(next(a)) # Вывод: 9 print(next(a)) # Вывод: 36 print(next(a)) # Вывод: 100 print(next(a)) # Вывод: StopIteration next(a)
Генераторные выражения могут быть использованы внутри функции. При таком использовании можно удалить скобки.
>>> sum(x**2 for x in my_list) 146 >>> max(x**2 for x in my_list) 100
Есть несколько причин, по которым генераторы представляют собой привлекательное решение.
Сравниваясь с соответствующими элементами класса итераторов, генераторы могут быть реализованы более klarо и кратко. Вот пример использования класса iterator для создания последовательности степеней 2.
class PowTwo: def __init__(self, max = 0): self.max = max def __iter__(self): self.n = 0 return self def __next__(self): if self.n > self.max: raise StopIteration result = 2 ** self.n self.n += 1 return result
Этот код очень длинный. Теперь выполним такую же операцию с помощью генераторной функции.
def PowTwoGen(max = 0): n = 0 while n < max: yield 2 ** n n += 1
Благодаря автоматическому отслеживанию подробностей генераторы просты и их реализация также проста.
Обычная функция, возвращающая последовательность, создает всю последовательность в памяти до того, как возвращает результат. Если количество элементов в последовательности велико, это может повлиять на эффективность.
Реализация генераторов для этой последовательности является памятинезависимой и поэтому предпочтительна, так как она может генерировать только одно значение за раз.
Генераторы являются отличным средством для представления потоков данных.
Ниже приведен пример, который может генерировать все чётные числа (теоретически, по крайней мере).
def all_even(): n = 0 while True: yield n n += 2
Генераторы могут использоваться для流水чной обработки последовательности операций. Лучше всего объяснить это с помощью примера.
Предположим, что у нас есть лог файл известной сети фастфуда. В файле есть колонка (четвертая колонка), которая отслеживает количество проданных пицц每小时, и мы хотим сложить их, чтобы получить общее количество проданных пицц за 5 лет.
Предположим, что все содержимое являются строками, и нет доступных чисел, отмеченных как " N / A". Реализация генератора может быть такой.
with open('sells.log') as file: pizza_col = (line[3] for line in file) per_hour = (int(x) for x in pizza_col if x != 'N/A') print("Общее количество проданных пицц = ", sum(per_hour))
Эта流水чная линия эффективна и легко читается (да, это очень круто!).