Генераторы Python
Генераторы Python
В этом руководстве вы узнаете, как легко создавать итерации с помощью генераторов Python, чем они отличаются от итераторов и обычных функций и почему вам следует их использовать.
Видео:Генераторы Python
Генераторы в Python
Существует много работы по созданию итератора в Python. Мы должны реализовать класс с __iter__()
и __next__()
метод, отслеживать внутренние состояния и поднимать StopIteration
когда нет возвращаемых значений.
Это и долго, и контринтуитивно. Генератор приходит на помощь в таких ситуациях.
Генераторы Python — это простой способ создания итераторов. Вся упомянутая выше работа автоматически выполняется генераторами Python.
Проще говоря, генератор — это функция, которая возвращает объект (итератор), который мы можем перебирать (по одному значению за раз).
<час>Создание генераторов в Python
Создать генератор в Python довольно просто. Это так же просто, как определить обычную функцию, но с yield
оператор вместо return
заявление.
Если функция содержит хотя бы один yield
оператор (может содержать другие yield
или return
операторы), она становится функцией-генератором. Оба yield
и return
вернет некоторое значение из функции.
Разница в том, что в то время как return
оператор полностью завершает функцию, yield
Оператор приостанавливает функцию, сохраняя все ее состояния, а затем продолжает выполнение последующих вызовов.
Различия между функцией генератора и обычной функцией
Вот чем функция-генератор отличается от обычной функции.
- Функция генератора содержит один или несколько
yield
заявления. - При вызове он возвращает объект (итератор), но не начинает выполнение немедленно.
- Методы типа
__iter__()
и__next__()
реализуются автоматически. Таким образом, мы можем перебирать элементы, используяnext()
. . - После выполнения функции функция приостанавливается, а управление передается вызывающей стороне.
- Локальные переменные и их состояния запоминаются между последовательными вызовами.
- Наконец, когда функция завершается,
StopIteration
поднимается автоматически при дальнейших вызовах.
Вот пример, иллюстрирующий все пункты, изложенные выше. У нас есть функция-генератор с именем my_gen()
. с несколькими yield
заявления.
# A simple generator function
def my_gen():
n = 1
print('This is printed first')
# Generator function contains yield statements
yield n
n += 1
print('This is printed second')
yield n
n += 1
print('This is printed at last')
yield n
Интерактивный запуск в интерпретаторе приведен ниже. Запустите их в оболочке Python, чтобы увидеть результат.
>>> # It returns an object but does not start execution immediately.
>>> a = my_gen()
>>> # We can iterate through the items using next().
>>> next(a)
This is printed first
1
>>> # Once the function yields, the function is paused and the control is transferred to the caller.
>>> # Local variables and theirs states are remembered between successive calls.
>>> next(a)
This is printed second
2
>>> next(a)
This is printed at last
3
>>> # Finally, when the function terminates, StopIteration is raised automatically on further calls.
>>> next(a)
Traceback (most recent call last):
...
StopIteration
>>> next(a)
Traceback (most recent call last):
...
StopIteration
В приведенном выше примере следует отметить одну интересную вещь:значение переменной n запоминается между каждым вызовом.
В отличие от обычных функций, локальные переменные не уничтожаются, когда функция уступает. Кроме того, объект-генератор можно повторить только один раз.
Чтобы перезапустить процесс, нам нужно создать еще один объект-генератор, используя что-то вроде a = my_gen()
. .
Последнее, что следует отметить, это то, что мы можем напрямую использовать генераторы с циклами for.
Это потому, что for
цикл принимает итератор и перебирает его, используя next()
функция. Он автоматически заканчивается, когда StopIteration
Поднялся. Проверьте здесь, чтобы узнать, как на самом деле реализован цикл for в Python.
# A simple generator function
def my_gen():
n = 1
print('This is printed first')
# Generator function contains yield statements
yield n
n += 1
print('This is printed second')
yield n
n += 1
print('This is printed at last')
yield n
# Using for loop
for item in my_gen():
print(item)
Когда вы запустите программу, вывод будет:
This is printed first 1 This is printed second 2 This is printed at last 3<час>
Генераторы Python с циклом
Приведенный выше пример менее полезен, и мы изучили его только для того, чтобы получить представление о том, что происходит в фоновом режиме.
Обычно функции генератора реализуются с циклом, имеющим подходящее условие завершения.
Давайте рассмотрим пример генератора, который переворачивает строку.
def rev_str(my_str):
length = len(my_str)
for i in range(length - 1, -1, -1):
yield my_str[i]
# For loop to reverse the string
for char in rev_str("hello"):
print(char)
Вывод
o l l e h
В этом примере мы использовали range()
функция для получения индекса в обратном порядке с помощью цикла for.
Примечание :эта функция-генератор работает не только со строками, но и с другими типами итерируемых объектов, такими как список, кортеж и т. д.
<час>Выражение генератора Python
Простые генераторы можно легко создавать на лету, используя выражения генератора. Это упрощает сборку генераторов.
Аналогично лямбда-функциям, которые создают анонимные функции, выражения-генераторы создают анонимные функции-генераторы.
Синтаксис выражения генератора аналогичен синтаксису спискового понимания в Python. Но квадратные скобки заменяются круглыми скобками.
Основное различие между генератором списков и генератором выражений заключается в том, что генератор списков создает весь список, а генератор создает по одному элементу за раз.
У них ленивое исполнение (производство предметов только по запросу). По этой причине генераторное выражение намного эффективнее использует память, чем эквивалентное понимание списка.
# Initialize the list
my_list = [1, 3, 6, 10]
# square each term using list comprehension
list_ = [x**2 for x in my_list]
# same thing can be done using a generator expression
# generator expressions are surrounded by parenthesis ()
generator = (x**2 for x in my_list)
print(list_)
print(generator)
Вывод
[1, 9, 36, 100] <generator object <genexpr> at 0x7f5d4eb4bf50>
Выше мы видим, что выражение генератора не сразу дало требуемый результат. Вместо этого он возвращает объект-генератор, который создает элементы только по требованию.
Вот как мы можем начать получать элементы из генератора:
# Initialize the list
my_list = [1, 3, 6, 10]
a = (x**2 for x in my_list)
print(next(a))
print(next(a))
print(next(a))
print(next(a))
next(a)
Когда мы запускаем указанную выше программу, мы получаем следующий вывод:
1 9 36 100 Traceback (most recent call last): File "<string>", line 15, in <module> StopIteration
Выражения генератора могут использоваться в качестве аргументов функции. При таком использовании круглые скобки можно опустить.
>>> sum(x**2 for x in my_list)
146
>>> max(x**2 for x in my_list)
100
<час> Использование генераторов Python
Есть несколько причин, которые делают генераторы мощной реализацией.
1. Легко реализовать
Генераторы могут быть реализованы ясным и лаконичным способом по сравнению с их аналогом класса итератора. Ниже приведен пример реализации последовательности степени 2 с использованием класса итератора.
class PowTwo:
def __init__(self, max=0):
self.n = 0
self.max = max
def __iter__(self):
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
Поскольку генераторы автоматически отслеживают детали, реализация была лаконична и намного чище.
2. Эффективное использование памяти
Обычная функция для возврата последовательности создаст всю последовательность в памяти перед возвратом результата. Это излишество, если количество элементов в последовательности очень велико.
Генераторная реализация таких последовательностей удобна для памяти и предпочтительнее, поскольку она создает только один элемент за раз.
3. Представлять бесконечный поток
Генераторы — отличные средства для представления бесконечного потока данных. Бесконечные потоки нельзя хранить в памяти, а поскольку генераторы производят только один элемент за раз, они могут представлять бесконечный поток данных.
Следующая функция генератора может генерировать все четные числа (по крайней мере, теоретически).
def all_even():
n = 0
while True:
yield n
n += 2
4. Конвейерные генераторы
Можно использовать несколько генераторов для конвейерной последовательности операций. Лучше всего это проиллюстрировать на примере.
Предположим, у нас есть генератор, который производит числа в ряду Фибоначчи. И у нас есть еще один генератор для возведения чисел в квадрат.
Если мы хотим узнать сумму квадратов чисел в ряду Фибоначчи, мы можем сделать это следующим образом, объединяя выходные данные генераторных функций.
def fibonacci_numbers(nums):
x, y = 0, 1
for _ in range(nums):
x, y = y, x+y
yield x
def square(nums):
for num in nums:
yield num**2
print(sum(square(fibonacci_numbers(10))))
Вывод
4895
Эта конвейерная обработка эффективна и легко читается (и да, намного круче!).
Python