There's nothing wrong here protocol-wise or in theory that would stop you from writing such code. An exhausted iterator it
will throw StopIteration
on every subsequent call to it.__next__
, so the for
loop technically won't mind if you exhaust the iterator with a next
/__next__
call in the loop body.
I advise against writing such code because the program will be very hard to reason about. If the scenario gets a little more complex than what you are showing here, at least I would have to go through some inputs with pen and paper and work out what's happening.
In fact, your code snippet possibly does not even behave like you think it behaves, assuming you want to print every number that is preceded by an even number.
>>> b = [1, 2, 4, 7, 8]
>>> a = iter(b)
>>> for x in a:
...: if x%2 == 0:
...: print(next(a, 'stop'))
4
stop
Why is 7
skipped although it's preceded by the even number 4
?
>>>> a = iter(b)
>>>> for x in a:
...: print('for loop assigned x={}'.format(x))
...: if x%2 == 0:
...: nxt = next(a, 'stop')
...: print('if popped nxt={} from iterator'.format(nxt))
...: print(nxt)
...:
for loop assigned x=1
for loop assigned x=2
if popped nxt=4 from iterator
4
for loop assigned x=7
for loop assigned x=8
if popped nxt=stop from iterator
stop
Turns out x = 4
is never assigned by the for
loop because the explicit next
call popped that element from the iterator before the for
loop had a chance to look at the iterator again.
That's something I'd hate to work out the details of when reading code.
If you want to iterate over an iterable (including iterators) in "(element, next_element)
" pairs, use the pairwise
recipe from the itertools
documentation.
from itertools import tee
def pairwise(iterable):
"s -> (s0,s1), (s1,s2), (s2, s3), ..."
a, b = tee(iterable)
next(b, None)
return zip(a, b)
Demo:
>>> b = [1,2,3,4,5,6,7]
>>> a = iter(b)
>>>
>>> for x, nxt in pairwise(a): # pairwise(b) also works
...: print(x, nxt)
1 2
2 3
3 4
4 5
5 6
6 7
In general, itertools
together with its recipes provides many powerful abstractions for writing readable iteration-related code. Even more useful helpers can be found in the more_itertools
module, including an implementation of pairwise
.
if
condition and always callnext(a)
?pairwise
recipe in timgeb's answer is a clearer way to do this.