Python: How to tell the for loop to continue from a function?

fmalina picture fmalina · May 20, 2011 · Viewed 19.3k times · Source

Sometimes I need the following pattern within a for loop. At times more than once in the same loop:

try:
    # attempt to do something that may diversely fail
except Exception as e:
    logging.error(e)
    continue

Now I don't see a nice way to wrap this in a function as it can not return continue:

def attempt(x):
    try:
        raise random.choice((ValueError, IndexError, TypeError))
    except Exception as e:
        logging.error(e)
        # continue  # syntax error: continue not properly in loop
        # return continue  # invalid syntax
        return None  # this sort of works

If I return None than I could:

a = attempt('to do something that may diversely fail')
if not a:
    continue

But I don't feel that does it the justice. I want to tell the for loop to continue (or fake it) from within attempt function.

Answer

Johnsyweb picture Johnsyweb · May 20, 2011

Python already has a very nice construct for doing just this and it doesn't use continue:

for i in range(10):
    try:
        r = 1.0 / (i % 2)
    except Exception, e:
        print(e)
    else:
        print(r)

I wouldn't nest any more than this, though, or your code will soon get very ugly.

In your case I would probably do something more like this as it is far easier to unit test the individual functions and flat is better than nested:

#!/usr/bin/env python

def something_that_may_raise(i):
    return 1.0 / (i % 2)

def handle(e):
    print("Exception: " + str(e))

def do_something_with(result):
    print("No exception: " + str(result))

def wrap_process(i):
    try:
        result = something_that_may_raise(i)
    except ZeroDivisionError, e:
        handle(e)
    except OverflowError, e:
        handle(e) # Realistically, this will be a different handler...
    else:
        do_something_with(result)

for i in range(10):
    wrap_process(i)

Remember to always catch specific exceptions. If you were not expecting a specific exception to be thrown, it is probably not safe to continue with your processing loop.

Edit following comments:

If you really don't want to handle the exceptions, which I still think is a bad idea, then catch all exceptions (except:) and instead of handle(e), just pass. At this point wrap_process() will end, skipping the else:-block where the real work is done, and you'll go to the next iteration of your for-loop.

Bear in mind, Errors should never pass silently.