How to call an iterator as if it was a function

Viewed 76

I need a watchdog mecanism to prevent infinite loops. This would look like this:

def stop(n):
    """ Returns False after n calls """ 
    for _ in range(n):
        yield True
    yield False

and I would use it this way:

def foo():
    # this is the function I have to watch
    return True # not good!

while foo() and stop(1000):
    # do some stuff

Of course the above code does not work because stop(1000) is an iterator. I could write a function that mimics stop(), by using a global variable, but it would not be very pythonic. I can also write like this:

for _ in range(n): 
    if not foo(): 
        break
    # do_some_stuff()

It works, but not very elegant, because range(n) is of secondary importance. Is there any Python magic that would make this code elegant?

3 Answers

Instantiate your generator as an object and call next():

def stop(n):
    """ Returns False after n calls """ 
    for _ in range(n):
        yield True
    yield False

my_stop = stop(1000)

while not foo() and next(my_stop):
    # do your things

This will ensure it keeps going back to the same instance of the generator and stop after 1000 attempts. Also from what I surmise it seems you meant to break when foo() returns True? If so the catch should be not foo() instead.

Although to be completely Pythonic, I'd argue just use a for loop:

# Just realized @timgeb answered the same alternative...
for _ in range(1000):
    if foo():
        break

Your stop() generator is literally doing the same thing as a for loop, and just returning whether it's still within the for loop.

You could import islice from itertools and write it like this:

for _ in islice(iter(foo, False), n):
    # do stuff

This will call foo as long as it does not produce False, but at most n times.

However, personally I like

for _ in range(n): 
    if not foo(): 
        break
    # do stuff

better. It's two lines longer but more readable.

Just use a counter.

count = 0
while not foo():
    count += 1
    if count > 1000:
        break

    # Do things and stuff here
Related