Reputation: 20361
Take a look at this, the crux of the question is at the bottom:
>>> scan = iter('FHUR203459')
>>> while True:
print(next(scan))
F
H
U
R
2
0
3
4
5
9
Traceback (most recent call last):
File "<pyshell#11>", line 2, in <module>
print(next(scan))
StopIteration
>>> scan = iter('FHUR203459')
>>> for i in range(12): # 12 * 2 for each join is 24, much longer than the string; should raise error.
print(''.join(next(scan) for i in range(2)))
FH
UR
20
34
59
>>>
In other words, we can see that the iterator reaches its end in both situations, however it only raises a StopIteration
in the first, even though next()
is used in both situations after it reached the end. Why does using it in join
seem to evade the error? Or is this a bug?
Upvotes: 2
Views: 572
Reputation: 239453
In the first case, the StopIteration
is not handled anywhere. But in the second case,
''.join(next(scan) for i in range(2))
we pass a generator expression to the ''.join
, which handles the StopIteration
raised by next(scan)
and exits every time. That is why ''.join
produces empty strings.
You can slightly modify the same, and pass a list to ''.join
and see the exception being raised, yourself, like this
>>> scan = iter('FHUR203459')
>>> for i in range(12):
... print(''.join([next(scan) for i in range(2)]))
...
FH
UR
20
34
59
Traceback (most recent call last):
File "<input>", line 2, in <module>
File "<input>", line 2, in <listcomp>
StopIteration
It shows that the StopIteration
is actually raised, and List Comprehension takes the hit, this time.
Upvotes: 2
Reputation: 1121654
str.join()
calls list()
on the generator, and that call swallows the StopIteration
.
Anything that consumes an iterator must catch StopIteration
; it doesn't matter then exactly what raised the exception; the generator expression or anything used by the generator expression:
>>> def raises_stopiteration(): raise StopIteration
...
>>> next(raises_stopiteration() for _ in range(10))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 1, in <genexpr>
File "<stdin>", line 1, in raises_stopiteration
StopIteration
>>> list(raises_stopiteration() for _ in range(10))
[]
Upvotes: 2