Is there a tidier alternative to `except: pass`?

I had a function that returned a random member from several groups in order of preference. It happened something like this:

def get_random_foo_or_bar(): "I'd rather have a foo than a bar." if there_are_foos(): return get_random_foo() if there_are_bars(): return get_random_bar() raise IndexError, "No foos, no bars" 

However, the first thing get_random_foo really checks is that foos exist and raise an IndexError if not, so there_are_foos is redundant. In addition, a database is used and the use of individual functions creates a concurrency problem. Accordingly, I rewrote it like this:

 def get_random_foo_or_bar(): "Still prefer foos." try: return get_random_foo() except IndexError: pass try: return get_random_bar() except IndexError: pass raise IndexError, "No foos, no bars" 

But I find this to be much less readable, and since I never had any reason to use pass before it feels impractical.

Is there a more efficient pattern, or should I learn to accept pass ?

Note. I would like to avoid any nesting, as other types can be added later.


Edit

Thanks to everyone who said that pass is fine - it is encouraging!

Also thanks to those who suggested replacing the exception with a None return value. I see how this is a useful template, but I would say that it is semantically incorrect in this situation: the functions were asked to perform an impossible task so that they throw an exception. I prefer to monitor the behavior of the random module (for example, random.choice([]) ).

+6
python coding-style exception-handling
source share
8 answers

That's exactly what I’ll write. It is simple and makes sense. I do not see problems with pass statements.

If you want to reduce repetition, and you expect future types to be added, you can flip this into a loop. You can then change the pass to a functionally equivalent continue expression if it is more pleasing to your eyes:

 for getter in (get_random_foo, get_random_bar): try: return getter() except IndexError: continue # Ignore the exception and try the next type. raise IndexError, "No foos, no bars" 
+13
source share

pass excellent (there is a reason this is in the language! -), but the alternative without a password just takes a little more nesting:

 try: return get_random_foo() except IndexError: try: return get_random_bar() except IndexError: raise IndexError "no foos, no bars" 

Python zen this wise koan! -) (Like in "if you meet Buddha on the road" ...).

+2
source share

It seems a little strange to me that get_random_foo() throws an IndexError when it does not accept the index as a parameter (but it may make more sense in context). Why not have get_random_foo() or a wrapper, catch an error and return None instead?

 def get_random_foo_wrapper(): try: return get_random_foo() except IndexError: return None def get_random_foo_or_bar(): "I'd rather have a foo than a bar." return get_random_foo_wrapper() or get_random_bar_wrapper() or None 

Change I should mention that if foo and bar are objects that can evaluate to False (0 or say), a comparison of or will skip them, which is BAD

+2
source share

If these are just these two, you can always just ...

 try: return get_random_foo() except IndexError: try: return get_random_bar() except IndexError: raise IndexError, "No foos, no bars" 

If this is more than two, then what you wrote seems perfectly acceptable.

+1
source share

Based on Peter Gibson's suggestion, you can create a general wrapper function that swallows this exception. And then you can write a function that returns such a common wrapper for the provided exception. Or heck, for the provided list of exceptions.

 def maketrap(*exceptions): def trap(func, *args, **kwargs): try: return func(*args, **kwargs) except exceptions: return None return trap def get_random_foo_or_bar(): mytrap = maketrap(IndexError) return mytrap(get_random_foo) or mytrap(get_random_bar) or None 
0
source share

If you really don't need an exception message (type only):

 def get_random_foo_or_bar(): try: return get_random_foo() except IndexError: return get_random_bar() # if failing at this point, # the whole function will raise IndexError 
0
source share

Does get_random_foo / bar () need to raise an IndexError if it cannot succeed?

If they returned None, you can do:

 def get_random_foo_or_bar(): return get_random_foo() or get_random_bar() 
0
source share

Using try other than pass is acceptable, but there is a cleaner way to write this using contextlib.suppress() available for python contextlib.suppress() .

 from contextlib import suppress def get_random_foo_or_bar(): "Still prefer foos." with suppress(IndexError): return get_random_foo() with suppress(IndexError): return get_random_bar() raise IndexError("No foos, no bars") 
0
source share

All Articles