Вот простой yield
подход, основанный на вычислении ряда Фибоначчи, пояснил:
def fib(limit=50):
a, b = 0, 1
for i in range(limit):
yield b
a, b = b, a+b
Когда вы введете это в свой REPL, а затем попытаетесь позвонить, вы получите загадочный результат:
>>> fib()
<generator object fib at 0x7fa38394e3b8>
Это потому, что наличие yield
сигнализирует Python, что вы хотите создать генератор , то есть объект, который генерирует значения по требованию.
Итак, как вы генерируете эти значения? Это можно сделать либо напрямую, используя встроенную функцию next
, либо косвенно, передав ее в конструкцию, которая потребляет значения.
Используя встроенную функцию next()
, вы напрямую вызываете .next
/ __next__
, заставляя генератор выдавать значение:
>>> g = fib()
>>> next(g)
1
>>> next(g)
1
>>> next(g)
2
>>> next(g)
3
>>> next(g)
5
Косвенно, если вы предоставите fib
для цикла for
, инициализатора list
, инициализатора tuple
или чего-либо еще, что ожидает объект, который генерирует / производит значения, вы будете "потреблять" генератор до тех пор, пока он не сможет произвести больше значений (и он вернется):
results = []
for i in fib(30): # consumes fib
results.append(i)
# can also be accomplished with
results = list(fib(30)) # consumes fib
Аналогично, с tuple
инициализатором:
>>> tuple(fib(5)) # consumes fib
(1, 1, 2, 3, 5)
Генератор отличается от функции в том смысле, что он ленив. Это достигается путем поддержания локального состояния и возобновления работы в любое время.
Когда вы впервые вызываете fib
, вызывая его:
f = fib()
Python компилирует функцию, встречает ключевое слово yield
и просто возвращает объект генератора обратно к вам. Не очень полезно, кажется.
Когда вы затем запрашиваете, он генерирует первое значение, прямо или косвенно, он выполняет все найденные операторы, пока не встретит yield
, затем возвращает значение, которое вы указали для yield
, и делает паузу. Для примера, который лучше демонстрирует это, давайте использовать несколько вызовов print
(замените на print "text"
, если на Python 2):
def yielder(value):
""" This is an infinite generator. Only use next on it """
while 1:
print("I'm going to generate the value for you")
print("Then I'll pause for a while")
yield value
print("Let's go through it again.")
Теперь введите в REPL:
>>> gen = yielder("Hello, yield!")
у вас есть объект-генератор, ожидающий команды для создания значения. Используйте next
и посмотрите, что напечатано:
>>> next(gen) # runs until it finds a yield
I'm going to generate the value for you
Then I'll pause for a while
'Hello, yield!'
Результаты без кавычек - это то, что напечатано. Результатом в кавычках является то, что возвращается из yield
. Звоните next
еще раз сейчас:
>>> next(gen) # continues from yield and runs again
Let's go through it again.
I'm going to generate the value for you
Then I'll pause for a while
'Hello, yield!'
Генератор запоминает, что он был приостановлен на yield value
, и возобновляется оттуда. Следующее сообщение печатается, и поиск оператора yield
для приостановки выполняется снова (из-за цикла while
).