Вступление

Генератор выражение подобно список, словарь и набор постижений, но заключено в круглых скобках. Скобки не обязательно должны присутствовать, когда они используются в качестве единственного аргумента для вызова функции.

 expression = (x**2 for x in range(10))

 

Этот пример генерирует 10 первых совершенных квадратов, включая 0 (в котором x = 0).

Функции генератора похожи на обычные функции, за исключением того, что они имеют один или более yield заявления в своем теле. Такие функции не могут return любые значения (однако пустое return s разрешены , если вы хотите , чтобы остановить генератор рано).

 def function():
    for x in range(10):
        yield x**2

 

Эта функция генератора эквивалентна предыдущему выражению генератора, она выводит то же самое.

Примечание: все выражения генератора имеют свои собственные эквивалентные функции, но не наоборот.

Выражение генератора можно использовать без скобок, если обе скобки будут повторяться в противном случае:

 sum(i for i in range(10) if i % 2 == 0)   #Output: 20
any(x = 0 for x in foo)                   #Output: True or False depending on foo
type(a > b for a in foo if a % 2 == 1)    #Output: <class 'generator'>

 

Вместо:

 sum((i for i in range(10) if i % 2 == 0))
any((x = 0 for x in foo))
type((a > b for a in foo if a % 2 == 1))

 

Но нет:

 fooFunction(i for i in range(10) if i % 2 == 0,foo,bar)
return x = 0 for x in foo
barFunction(baz, a > b for a in foo if a % 2 == 1)

 

Вызов функции генератора создает объект генератора, который впоследствии может перемещаться. В отличие от других типов итераторов, объекты-генераторы могут быть пройдены только один раз.

 g1 = function()
print(g1)  # Out: <generator object function at 0x1012e1888>

 

Обратите внимание на то, что тело генератора не выполняется сразу же: при вызове function() в примере выше, она немедленно возвращает объект генератора, не выполняя даже первый оператор печати. Это позволяет генераторам использовать меньше памяти, чем функциям, которые возвращают список, и позволяет создавать генераторы, которые создают бесконечно длинные последовательности.

По этой причине генераторы часто используются в науке о данных и других контекстах, связанных с большими объемами данных. Другое преимущество состоит в том, что другой код может немедленно использовать значения, полученные генератором, не дожидаясь полной последовательности, которая будет произведена.

Тем не менее, если вам нужно использовать значения , полученные с помощью генератора более чем один раз, и если их генерации стоит больше , чем хранение, может быть лучше хранить получены значения в list , чем повторно генерировать последовательность. См. «Сброс генератора» ниже для более подробной информации.

Обычно объект генератора используется в цикле или в любой функции, которая требует итерации:

 for x in g1:
    print("Received", x)

# Output:
# Received 0
# Received 1
# Received 4
# Received 9
# Received 16
# Received 25
# Received 36
# Received 49
# Received 64
# Received 81

arr1 = list(g1)
# arr1 = [], because the loop above already consumed all the values.
g2 = function()
arr2 = list(g2)  # arr2 = [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

 

Так как объекты генератора итераторы, можно итерации по их вручную с помощью next() функции. Это вернет полученные значения одно за другим при каждом последующем вызове.

Под капотом, каждый раз , когда вы звоните next() на генераторе, Python выполняет операторы в теле функции генератора , пока он не достигнет следующей yield заявление. В этот момент она возвращает аргумент yield команды, и запоминает место , где это произошло. Вызов next() еще раз возобновить выполнение с этого момента и продолжается до следующего yield заявления.

Если Python достигает конца функции генератора не встречая больше yield S, A StopIteration возбуждается исключение (это нормально, все итераторы ведут себя таким же образом).

 g3 = function()
a = next(g3)  # a becomes 0
b = next(g3)  # b becomes 1
c = next(g3)  # c becomes 2
...
j = next(g3)  # Raises StopIteration, j remains undefined

 

Обратите внимание , что в Python 2 объекты генератор имел .next() методы , которые могут быть использованы для перебора значений , полученных в результате вручную. В Python 3 этот метод был заменен .__next__() стандартом для всех итераторов.

Сброс генератора

Помните , что вы можете перемещаться только по объектам , генерируемых генератором один раз. Если вы уже итерации по объектам в скрипте, любая дальнейшая попытка сделать это не даст None .

Если вам нужно использовать объекты, сгенерированные генератором более одного раза, вы можете либо снова определить функцию генератора и использовать ее во второй раз, либо, альтернативно, вы можете сохранить выходные данные функции генератора в списке при первом использовании. Переопределение функции генератора будет хорошим вариантом, если вы имеете дело с большими объемами данных, а сохранение списка всех элементов данных займет много места на диске. И наоборот, если изначально создавать элементы дорого, вы можете предпочесть сохранить сгенерированные элементы в списке, чтобы их можно было использовать повторно.