Reputation: 46533
This is my code
class A:
pass
def f():
yield A()
def g():
it = f()
next(it).a = next(it, None)
g()
that produces the StopIteration
error, caused by next(it).a = next(it, None)
. Why?
The documentation says that next
function does not raise the StopIteration
if the default value is provided, and I expected it to get me the first item from the generator (the A
instance) and set the a
attribute to None
.
Upvotes: 2
Views: 5753
Reputation: 1121724
Your f()
generator function yields just one value. After that it is exhausted and raises StopIteration
.
>>> class A:
... pass
...
>>> def f():
... yield A()
...
>>> generator = f()
>>> generator
<generator object f at 0x10be771f8>
>>> next(generator)
<__main__.A object at 0x10be76f60>
>>> next(generator)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
That's because there is no loop in f()
to yield more than once, a generator function does not, on its own, loop, just because you can use it in a loop.
Note that for an assignment, Python executes the right-hand-side expression first, before figuring out what to assign it to. So the next(it, None)
is called first, the next(it).a
for the assignment is called second.
The body of the f()
function is executed just like any other Python function, with the addition of pausing. next()
on the generator un-pauses the code, and the code then runs until the next yield
statement is executed. That statement then pauses the generator again. If the function instead ends (returns), StopIteration
is raised.
In your f()
generator that means:
f()
a new generator object is created. The function body is paused.next()
on it the first time. The code starts running, creates an instance of A()
and yields that instance. The function is paused again.next()
on it a second time. The code starts running, reaches the end of the function, and returns. StopIteration
is raised.If you add a loop to f()
, or simply add a second yield
line, your code works:
def f():
yield A()
yield A()
or
def f():
while True:
yield A()
Upvotes: 5
Reputation: 10224
Because f
only yields a single value, you can only call next
on it once.
The right hand side of your expression (next(it, None)
) is evaluated before the left hand side, and thus exhausts the generator.
Calling next(it).a
on the left hand side will then raise StopIteration
.
Upvotes: 9