Here's is my generator function code:
def fibo():
n1=0
n2=1
while True:
if n1 == 8:
raise StopIteration
else:
yield n1
n1,n2=n2,n1+n2
seq=fibo()
Here is my code version-1 for generating sequence:
for ele in seq:
print(next(seq))
Output:
1
2
5
RuntimeError: generator raised StopIteration
---------------------------------------------------------------------------
StopIteration Traceback (most recent call last)
<ipython-input-3-c01815b93e23> in fibo()
5 if n1 == 8:
----> 6 raise StopIteration
7 else:
StopIteration:
The above exception was the direct cause of the following exception:
RuntimeError Traceback (most recent call last)
<ipython-input-3-c01815b93e23> in <module>
11 seq=fibo()
12
---> 13 for ele in seq:
14 print(next(seq))
15
RuntimeError: generator raised StopIteration
My expected output for version-1:
0
1
1
2
3
5
Here is my code version-2 for generating sequence:
while True:
try:
print(next(seq))
except StopIteration:
print('This exception is raised in the generator operation defining function. Hence it is handled here.')
break
Output:
0
1
1
2
3
5
RuntimeError: generator raised StopIteration
---------------------------------------------------------------------------
StopIteration Traceback (most recent call last)
<ipython-input-4-6afe26583a33> in fibo()
5 if n1 == 8:
----> 6 raise StopIteration
7 else:
StopIteration:
The above exception was the direct cause of the following exception:
RuntimeError Traceback (most recent call last)
<ipython-input-4-6afe26583a33> in <module>
16 while True:
17 try:
---> 18 print(next(seq))
19 except StopIteration:
20 print('This exception is raised in the generator operation defining function. Hence it is handled here.')
RuntimeError: generator raised StopIteration
My expected output for version-2:
0
1
1
2
3
5
This exception is raised in the generator operation defining function. Hence it is handled here.
In the first version, out is incorrect and exception is happening. In the second, in addition to correct output, same exception as version-1 is happening. Please help me to rectify this.
Here is my code version-1 for generating sequence:
The point of the for
loop construct in Python is that it already calls next
on the iterator (which it creates from the generator automatically). So you should just print(ele)
instead.
RuntimeError: generator raised StopIteration
Yes, because you are aren't supposed to use explicit raise StopIteration
in generators. That's an old tool for communicating to the for
loop that you're out of elements, which is only needed when you implement an __iter__
method the old-fashioned way. Yes, the for
loop construct depends on this exception to determine the end of the input; but the iterator created from your generator will do this automatically - and also check for StopIteration
s raised in the generator and convert them to RuntimeError
, explicitly to prevent your StopIteration
from breaking out of loops.
If the underlying machinery didn't have that check, then you could do things like for i in itertools.chain(my_generator, a_list):
, and the exception raised by the generator implementation would forcibly break the loop (after all, the loop exits by handling that exception) and prevent the a_list
elements from being seen. That's not how you want the generator to behave, and it's also in some sense a security risk, so that check is there.
If you want to signal that a generator is done yield
ing, what you do is... get to the end of the code. For example, with return
, or in this case break
also works (since there is nothing else after the loop):
def fibo():
n1, n2 = 0, 1
while True:
if n1 == 8:
break
else:
yield n1
n1, n2 = n2, n1+n2
print('This exception is raised in the generator operation defining function. Hence it is handled here.')
The reason that doesn't happen is that you are trying to catch StopIteration
(which you should not do because it would interfere with the loop's operation!), but the exception was (which you would know if you carefully read the stack trace) replaced with RuntimeError
instead. See above.
With the corrected code above, everything works as expected:
>>> list(fibo())
[0, 1, 1, 2, 3, 5]
>>> x = fibo()
>>> for f in x:
... print(f)
...
0
1
1
2
3
5
>>> x = fibo()
>>> while True:
... try:
... print(next(x))
... except StopIteration:
... print('this works as intended - but a for loop is much cleaner')
... break
...
0
1
1
2
3
5
this works as intended - but a for loop is much cleaner