21

Is it possible to use a generator or iterator in a while loop in Python? For example, something like:

i = iter(range(10))
while next(i):
    # your code

The point of this would be to build iteration into the while loop statement, making it similar to a for loop, with the difference being that you can now additional logic into the while statement:

i = iter(range(10))
while next(i) and {some other logic}:
    # your code

It then becomes a nice for loop/while loop hybrid.

Does anyone know how to do this?

khelwood
  • 55,782
  • 14
  • 81
  • 108
Union find
  • 7,759
  • 13
  • 60
  • 111
  • has to be a while-loop? `for x in i` would be the natural way. – user2390182 Nov 28 '19 at 15:59
  • I guess you would not try your idea on `it = itertools.count(); while next(it):` – RomanPerekhrest Nov 28 '19 at 16:03
  • can you give more context on your use case? – Chris_Rands Nov 28 '19 at 16:07
  • how would you access the value the iter returned since you dont store it you just take the item and evaluaate its boolean result so what would you do in the loop since you dont have access to the item returned from the iter? – Chris Doyle Nov 28 '19 at 16:08
  • What you need to elaborate is what should be a stopping factor of `while` loop. How about `it = iter([True, False, 1, 0, 1]); while next(it): print('1')` ? The answers given are simple, but what's your exact intention? – RomanPerekhrest Nov 28 '19 at 16:10
  • 1
    What if we wanted to keep this general though, which is why I didn't indicate a specific additional logic. The stopping factor here can just be assumed to be exceeding the last iteration to where the iterator throws an exception. @RomanPerekhrest – Union find Nov 28 '19 at 16:12

5 Answers5

32

In Python >= 3.8, you can do the following, using assignment expressions:

i = iter(range(10))
while (x := next(i, None)) is not None and x < 5:
    print(x)

In Python < 3.8 you can use itertools.takewhile:

from itertools import takewhile

i = iter(range(10))
for x in takewhile({some logic}, i):
    # do stuff

"Some logic" here would be a 1-arg callable receciving whatever next(i) yields:

for x in takewhile(lambda e: 5 > e, i):
    print(x)
0
1
2
3
4
user2390182
  • 72,016
  • 6
  • 67
  • 89
12

There are two problems with while next(i):

  1. Unlike a for loop, the while loop will not catch the StopIteration exception that is raised if there is no next value; you could use next(i, None) to return a "falsey" value in that case, but then the while loop will also stop whenever the iterator returns an actual falsey value
  2. The value returned by next will be consumed and no longer available in the loop's body. (In Python 3.8+, that could be solved with an assignment expression, see other answer.)

Instead, you could use a for loop with itertools.takewhile, testing the current element from the iterable, or just any other condition. This will loop until either the iterable is exhausted, or the condition evaluates to false.

from itertools import takewhile
i = iter(range(10))
r = 0
for x in takewhile(lambda x: r < 10, i):
    print("using", x)
    r += x
print("result", r)

Output:

using 0
...
using 4
result 10
tobias_k
  • 81,265
  • 12
  • 120
  • 179
5

You just need to arrange for your iterator to return a false-like value when it expires. E.g., if we reverse the range so that it counts down to 0:

>>> i = iter(range(5, -1, -1))
>>> while val := next(i):
...     print('doing something here with value', val)
...

This will result in:

doing something here with value 5
doing something here with value 4
doing something here with value 3
doing something here with value 2
doing something here with value 1
larsks
  • 277,717
  • 41
  • 399
  • 399
  • 2
    But this consumes `next(i)`, and if you call it again in the loop body you miss one value. Maybe with Python 3.8 assignment expressions, though. – tobias_k Nov 28 '19 at 16:02
  • Sure, this would be a prefect place to use assignment expressions. It wasn't clear from the question that you care about the value of the iterator. – larsks Nov 28 '19 at 16:15
  • Updated the example to use an assignment expression. – larsks Nov 28 '19 at 16:17
2
a = iter(range(10))

try:
    next(a)
    while True:
        print(next(a))
except StopIteration:
    print("Stop iteration")
kwa
  • 49
  • 4
  • 1
    Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Dec 05 '21 at 22:17
-3

You can do

    a = iter(range(10))

    try:
        a.next()
        while True and {True or False logic}:
            print("Bonjour")
            a.next()
    except StopIteration:
        print("a.next() Stop iteration")
Union find
  • 7,759
  • 13
  • 60
  • 111
TOTO
  • 307
  • 1
  • 6