Domanda

If I want to try many way to avoid some error, I may write:

try:

    try:
        trial_1()
    except some_error:
        try:
            trial_2()
        except some_error:
            try:
                trial_3()
            ...
    print "finally pass"

except some_error:
    print "still fail"

But there are too many trials so too many nest, how to write it in a flat style?

È stato utile?

Soluzione 2

You could do this:

def trial1 (): 42 / 0
def trial2 (): [] [42]
def trial3 (): 'yoohoo!'
def trial4 (): 'here be dragons'

for t in [trial1, trial2, trial3, trial4]:
    print ('Trying {}.'.format (t.__name__) )
    try:
        t ()
        print ('Success')
        break
    except Exception as ex:
        print ('Failed due to {}'.format (ex) )
else:
    print ('Epic fail!')

Output is:

Trying trial1.
Failed due to division by zero
Trying trial2.
Failed due to list index out of range
Trying trial3.
Success

Altri suggerimenti

If it's the same exception each time, you could do

for task in (trial_1, trial_2, trial_3, ...):
    try:
        task()
        break
    except some_error:
        continue

If knowing whether it succeeded is important, the clearest way to add that is probably

successful = False
for task in (trial_1, trial_2, trial_3, ...):
    try:
        task()
        successful = True
        break
    except some_error:
        continue
if successful:
    ...
else:
    ...

Assuming that a) each trial is different, and b) they all throw the same error (since that's what your code illustrates), and c) you know the names of all the trial functions:

for t in (trial_1, trial_2, trial_3, trial_4):
    try:
        t()
        # if we succeed, the trial is over
        break
    except some_error:
        continue

This will loop over every trial, continuing in the case of the expected error, stopping if the trial succeeds, and throwing any other exceptions. I think that's the same behavior of your example code.

If you need to do this more than once, you can wrap up the answers Hyperboreus and others gave as a function:

def first_success(*callables):
    for f in callables:
        try:
            return f()
        except Exception as x:
            print('{} failed due to {}'.format(f.__name__, x))
    raise RuntimeError("still fail")

Then, all you need is:

first_success(trial_1, trial_2, trial_3, trial_4)

If you want to logging.info the exceptions instead of print them, or ignore them entirely, or keep track of them and attach the list of exceptions to the return value and/or exception as an attribute, etc., it should be pretty obvious how to modify this.

If you want to pass arguments to the functions, that's not quite as obvious, but still pretty easy. You just need to decide what the interface should be. Maybe take a sequence of callables as the first argument, and then all the callables' arguments after that:

first_success((trial_1, trial_2, trial_3, trial_4), 42, spam='spam')

That's easy:

def first_success(callables, *args, **kwargs):
    for f in callables:
        try:
            return f(*args, **kwargs)
        except Exception as x:
            print('{} failed due to {}'.format(f.__name__, x))
    else:
        raise RuntimeError("still fail")

If you don't need exactly this pattern all the time, but you need a ton of not-quite-the-same things, you may want to instead write a function that just wraps any function in a try. I've actually built this half a dozen times, and then realized there was a more pythonic way to write my code that made this function unnecessary, so the only use I've ever gotten out of it was in arguments with Haskell snobs, but you may find a better use for it:

def tried(callable, *args, **kwargs):
    try:
        return (callable(*args, **kwargs), None)
    except Exception as x:
        return (None, x)

Now you can use higher-order functions like map, any, etc. For example, map(tried, (trial_1, trial_2, trial_3, trial_4)) gives you a sequence of four non-throwing functions, and you can (f(x[0]) if x[1] is None else x for x in tried_sequence) to work through a Haskell monad tutorial in Python, which is a good way to make both Python programmers and Haskell programmers hate you.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top