Consider the following code:
def mygen():
yield (yield 1)
a = mygen()
print(next(a))
print(next(a))
The output yields:
1
None
What does the interpreter do at the "outside" yield exactly?
The YIELD Function[1] is categorized under Excel Financial functions. It will calculate the yield on a security that pays periodic interest. The function is generally used to calculate bond yield. As a financial analyst, we often calculate the yield on a bond to determine the income that would be generated in a year.
The yield keyword pauses generator function execution and the value of the expression following the yield keyword is returned to the generator's caller. It can be thought of as a generator-based version of the return keyword. yield can only be called directly from the generator function that contains it.
Yes, it is still a generator. An empty return or return None can be used to end a generator function. It is equivalent to raising a StopIteration (see @NPE's answer for details). Note that a return with non-None arguments is a SyntaxError in Python versions prior to 3.3.
Since pytest-3.0, fixtures using the normal fixture decorator can use a yield statement to provide fixture values and execute teardown code, exactly like yield_fixture in previous versions.
a
is a generator object. The first time you call next
on it, the body is evaluated up to the first yield
expression (that is, the first to be evaluated: the inner one). That yield
produces the value 1
for next
to return, then blocks until the next entry into the generator. That is produced by the second call to next
, which does not send any value into the generator. As a result, the first (inner) yield
evaluates to None
. That value is used as the argument for the outer yield
, which becomes the return value of the second call to next
. If you were to call next
a third time, you would get a StopIteration
exception.
Compare the use of the send
method (instead of next
) to change the return value of the first yield
expression.
>>> a = mygen()
>>> next(a)
1
>>> a.send(3) # instead of next(a)
3
>>> next(a)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
A more explicit way of writing the generator would have been
def mygen():
x = yield 1
yield x
a = mygen()
print(a.send(None)) # outputs 1, from yield 1
print(a.send(5)) # makes yield 1 == 5, then gets 5 back from yield x
print(a.send(3)) # Raises StopIteration, as there's nothing after yield x
Prior to Python 2.5, the yield
statement provided one-way communication between a caller and a generator; a call to next
would execute the generator up to the next yield
statement, and the value provided by the yield
keyword would serve as the return value of next
. The generator
would also suspend at the point of the yield
statement, waiting for the next call to next
to resume.
In Python 2.5, the yield
statement was replaced* with the yield
expression, and generators acquired a send
method. send
works very much like next
, except it can take an argument. (For the rest of this, assume that next(a)
is equivalent to a.send(None)
.) A generator starts execution after a call to send(None)
, at which point it executes up to the first yield
, which returns a value as before. Now, however, the expression blocks until the next call to send
, at which point the yield
expression evaluates to the argument passed to send
. A generator can now receive a value when it resumes.
* Not quite replaced; kojiro's answer goes into more detail about the subtle difference between a yield
statement and yield
expression.
yield
has two forms, expressions and statements. They're mostly the same, but I most often see them in the statement
form, where the result would not be used.
def f():
yield a thing
But in the expression form, yield
has a value:
def f():
y = yield a thing
In your question, you're using both forms:
def f():
yield ( # statement
yield 1 # expression
)
When you iterate over the resulting generator, you get first the result of the inner yield expression
>>> x=f()
>>> next(x)
1
At this point, the inner expression has also produced a value that the outer statement can use
>>> next(x)
>>> # None
and now you've exhausted the generator
>>> next(x)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
To understand more about statements vs expressions, there are good answers in other stackoverflow questions: What is the difference between an expression and a statement in Python?
>>> def mygen():
... yield (yield 1)
...
>>> a = mygen()
>>>
>>> a.send(None)
1
>>> a.send(5)
5
>>> a.send(2)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
>>>
>>>
>>>
>>> def mygen():
... yield 1
...
>>> def mygen2():
... yield (yield 1)
...
>>> def mygen3():
... yield (yield (yield 1))
...
>>> a = mygen()
>>> a2 = mygen2()
>>> a3 = mygen3()
>>>
>>> a.send(None)
1
>>> a.send(0)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
>>> a2.send(None)
1
>>> a2.send(0)
0
>>> a2.send(1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
>>> a3.send(None)
1
>>> a3.send(0)
0
>>> a3.send(1)
1
>>> a3.send(2)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
>>>
Every other yield simply waits for a value to be passed into, generator don't only give data but they also receive it.
>>> def mygen():
... print('Wait for first input')
... x = yield # this is what we get from send
... print(x, 'is received')
...
>>> a = mygen()
>>> a.send(None)
Wait for first input
>>> a.send('bla')
bla is received
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
>>>
yield
gives the next value when you continue if you get it, and if it is not used for giving the next value, it is being used for receiving the next
>>> def mygen():
... print('Wait for first input')
... x = yield # this is what we get from send
... yield x*2 # this is what we give
...
>>> a = mygen()
>>> a.send(None)
Wait for first input
>>> a.send(5)
10
>>>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With