6

I wrote a generator, which returns tuple of values:

import numpy as np

def mygenerator():
    data = np.arange(10)
    data = np.reshape(data, (-1, 2))
    for row in data:
        yield row[0], row[1]

generator = mygenerator()
while True:
    a, b = generator.__next__()
    print("a: ", a, "b: ", b)

and want to use it without for ... in. How detect end of generation then?

Dims
  • 47,675
  • 117
  • 331
  • 600
  • 1
    Just keep calling `next` on it and manually catch `StopIteration`? – Jon Clements Sep 12 '17 at 09:44
  • Bit of info in https://stackoverflow.com/a/14413978... – Jon Clements Sep 12 '17 at 09:47
  • 1
    I know it may be an annoying question, but why can you not use a for loop? Mind showing us a bit more code to see if it is really impossible? – Reut Sharabani Sep 12 '17 at 09:47
  • Am also a bit surprised to learn what is probably the [answer](https://stackoverflow.com/a/1966609/4288043) to this question. – cardamom Sep 12 '17 at 09:49
  • [In Python 3, you should now be calling `next(generator)`, not `gen.__next__()` method](https://docs.python.org/3/library/functions.html?highlight=next#next). So you shouldn't need to detect generator end, explicitly. This syntax has an optional `next(generator, None)` to make iterating with for-loops cleaner. – smci Jul 23 '23 at 04:36

3 Answers3

21

The generator will throw an exception StopIteration when it is exhausted. You can catch the exception with tryexcept statement and exit loop when it occurs:

while True:
    try:
        a, b = next(generator)
    except StopIteration:
        break
    print("a: ", a, "b: ", b)

BTW, why not for loop? It seems that:

for a, b in generator:
    print("a: ", a, "b: ", b)

does exactly what you want, without cumbersome constructions.

Błotosmętek
  • 12,717
  • 19
  • 29
  • 3
    I can't use `for ... in` because in my real code I use complex processing of returned values and switching different loops... – Dims Sep 12 '17 at 10:03
  • @Dims ok you say you have a complex use-case which makes it hard for you to use a for-loop. But it's still recommended idiom. You could simplify it by defining helper subiterators, and/or chaining them. Post a new question showing that code if possible. – smci Jul 23 '23 at 04:34
1

Another possibility would be by using the default parameter of next built-in function as a breaking condition. Once the generator is exhausted, instead of throwing a StopIteration exception, the default value will be emitted.

# ...

gen = mygenerator()

stop_value = None # or another value
while True:
    v = next(gen, stop_value)
    if v is stop_value:
        break
    print("a: {}, b: {}".format(*v))
cards
  • 3,936
  • 1
  • 7
  • 25
-1

Stumped into this and suggest do following, generator will exhaust until next() returns None:

import numpy as np

def mygenerator():
    data = np.arange(10)
    data = np.reshape(data, (-1, 2))
    for row in data:
        yield row[0], row[1]

generator = mygenerator()
a, b = next(generator, None)
while a and b:
    print("a: ", a, "b: ", b)
    a, b = next(generator, None)
Greenonline
  • 1,330
  • 8
  • 23
  • 31
jaydrill
  • 54
  • 8