catching exceptions from an except: block

  • Thread starter Arnaud Delobelle
  • Start date
A

Arnaud Delobelle

Hi all,

Imagine I have three functions a(x), b(x), c(x) that each return
something or raise an exception. Imagine I want to define a function
that returns a(x) if possible, otherwise b(x), otherwise c(x),
otherwise raise CantDoIt.

Here are three ways I can think of doing it:

----------
# This one looks ugly
def nested_first(x):
try:
return a(x)
except:
try:
return b(x)
except:
try:
return c(x)
except:
raise CantDoIt

# This one looks long-winded
def flat_first(x):
try:
return a(x)
except:
pass
try:
return b(x)
except:
pass
try:
return c(x)
except:
raise CantDoIt

# This one only works because a,b,c are functions
# Moreover it seems like an abuse of a loop construct to me
def rolled_first(x):
for f in a, b, c:
try:
return f(x)
except:
continue
raise CantDoIt
----------

I don't feel happy with any of these. Is there a more satisfying way
of doing this in Python? What I would like is something like:

----------
# This one isn't correct but looks the clearest to me
def wished_first(x):
try:
return a(x)
except:
return b(x)
except:
return c(x)
except:
raise CantDoIt
----------

I guess what I'm looking for is some sort of
if:
elif:
....
elif:
else:

but for try: except:
That's why
try:
except:
except:
....
except:

seemed natural to me :) And I'd like to find a nice way to do this in
a syntactically correct way.

Note: I've chosen functions a, b, c, but really I'm looking for a way
that is suitable for any chunk of code.
 
M

Miki

Hello Arnaud,
Imagine I have three functions a(x), b(x), c(x) that each return
something or raise an exception. Imagine I want to define a function
that returns a(x) if possible, otherwise b(x), otherwise c(x),
otherwise raise CantDoIt.
Exceptions are for error handling, not flow control.
Here are three ways I can think of doing it:
...
# This one only works because a,b,c are functions
# Moreover it seems like an abuse of a loop construct to me
def rolled_first(x):
for f in a, b, c:
try:
return f(x)
except:
continue
raise CantDoIt
My vote is for that one.
I don't feel happy with any of these. Is there a more satisfying way
of doing this in Python? What I would like is something like:

----------
# This one isn't correct but looks the clearest to me
def wished_first(x):
try:
return a(x)
except:
return b(x)
except:
return c(x)
except:
raise CantDoIt
Again, exception are for error handling, not for flow control.

As a side note, try to avoid "catch:", always catch explicit
exceptions.

HTH,
Miki <[email protected]>
http://pythonwise.blogspot.com
 
A

Arnaud Delobelle

Hello Arnaud,

Hi Miki

[snip]
Exceptions are for error handling, not flow control.

Maybe but it's not always that clear cut! As error handling is a form
of flow control the two have to meet somewhere.

[snip]
As a side note, try to avoid "catch:", always catch explicit
exceptions.

I didn't specify what I wanted to catch because it didn't feel it was
relevant to the problem.

Thanks
 
M

Marc 'BlackJack' Rintsch

Arnaud Delobelle said:
# This one only works because a,b,c are functions
# Moreover it seems like an abuse of a loop construct to me
def rolled_first(x):
for f in a, b, c:
try:
return f(x)
except:
continue
raise CantDoIt
----------

Why do you think this is an abuse? I think it's a perfectly valid use of
a loop.

Ciao,
Marc 'BlackJack' Rintsch
 
M

Marc 'BlackJack' Rintsch

Exceptions are for error handling, not flow control.

That's not true, they are *exceptions* not *errors*. They are meant to
signal exceptional situations. And at least under the cover it's used in
every ``for``-loop because the end condition is signaled by a
`StopIteration` exception. Looks like flow control to me.

Ciao,
Marc 'BlackJack' Rintsch
 
L

Larry Bates

Arnaud said:
Hi all,

Imagine I have three functions a(x), b(x), c(x) that each return
something or raise an exception. Imagine I want to define a function
that returns a(x) if possible, otherwise b(x), otherwise c(x),
otherwise raise CantDoIt.

Here are three ways I can think of doing it:

----------
# This one looks ugly
def nested_first(x):
try:
return a(x)
except:
try:
return b(x)
except:
try:
return c(x)
except:
raise CantDoIt

# This one looks long-winded
def flat_first(x):
try:
return a(x)
except:
pass
try:
return b(x)
except:
pass
try:
return c(x)
except:
raise CantDoIt

# This one only works because a,b,c are functions
# Moreover it seems like an abuse of a loop construct to me
def rolled_first(x):
for f in a, b, c:
try:
return f(x)
except:
continue
raise CantDoIt
----------

I don't feel happy with any of these. Is there a more satisfying way
of doing this in Python? What I would like is something like:

----------
# This one isn't correct but looks the clearest to me
def wished_first(x):
try:
return a(x)
except:
return b(x)
except:
return c(x)
except:
raise CantDoIt
----------

I guess what I'm looking for is some sort of
if:
elif:
...
elif:
else:

but for try: except:
That's why
try:
except:
except:
...
except:

seemed natural to me :) And I'd like to find a nice way to do this in
a syntactically correct way.

Note: I've chosen functions a, b, c, but really I'm looking for a way
that is suitable for any chunk of code.
Without knowing more about the functions and the variable it is somewhat
hard to tell what you are trying to accomplish. If a, b, c are functions
that act on x when it is a different type, change to one function that
can handle all types.

def d(x):
if isinstance(x, basestring):
#
# Code here for string
#
elif isinstance(x, int):
#
# Code here for int
#
elif isinstance(x, float):
#
# Code here for string
#
else:
raise ValueError


If they are different functions based on type do something like this:

#
# Set up a dictionary with keys for different types and functions
# that correspond.
#
fdict={type(''): a, type(1): b, type(1.0): c}
#
# Call the appropriate function based on type
#
fdict[type(x)](x)

-Larry
 
G

Gabriel Genellina

En Wed, 07 Mar 2007 19:00:59 -0300, Bruno Desthuilliers
this kind of cose is exactly what OO polymorphic dispatch is supposed to

this kind of cose? Ce genre de chose?
 
A

Arnaud Delobelle

Without knowing more about the functions and the variable it is somewhat
hard to tell what you are trying to accomplish. If a, b, c are functions
that act on x when it is a different type, change to one function that
can handle all types.

I'm not really thinking about this situation so let me clarify. Here
is a simple concrete example, taking the following for the functions
a,b,c I mention in my original post.
- a=int
- b=float
- c=complex
- x is a string
This means I want to convert x to an int if possible, otherwise a
float, otherwise a complex, otherwise raise CantDoIt.

I can do:

for f in int, float, complex:
try:
return f(x)
except ValueError:
continue
raise CantDoIt

But if the three things I want to do are not callable objects but
chunks of code this method is awkward because you have to create
functions simply in order to be able to loop over them (this is whay I
was talking about 'abusing loop constructs'). Besides I am not happy
with the other two idioms I can think of.
 
B

Bruno Desthuilliers

Miki a écrit :
Hello Arnaud,



Exceptions are for error handling, not flow control.

def until(iterable,sentinel):
for item in iterable:
if item == sentinel:
raise StopIteration
yield item
.... print item
....
0
1
2
3
4
Exceptions *are* a form of flow control.
 
B

Bruno Desthuilliers

Larry Bates a écrit :
(snip)
def d(x):
if isinstance(x, basestring):
#
# Code here for string
#
elif isinstance(x, int):
#
# Code here for int
#
elif isinstance(x, float):
#
# Code here for string
#
else:
raise ValueError

As a side note : While there are a few corner cases where this is hardly
avoidable (and yet I'd rather test on interface, not on concrete type),
this kind of cose is exactly what OO polymorphic dispatch is supposed to
avoid (no, don't tell me: I know you can't easily add methods to most
builtin types).
 
G

garrickp

I'm not really thinking about this situation so let me clarify. Here
is a simple concrete example, taking the following for the functions
a,b,c I mention in my original post.
- a=int
- b=float
- c=complex
- x is a string
This means I want to convert x to an int if possible, otherwise a
float, otherwise a complex, otherwise raise CantDoIt.

I can do:

for f in int, float, complex:
try:
return f(x)
except ValueError:
continue
raise CantDoIt

But if the three things I want to do are not callable objects but
chunks of code this method is awkward because you have to create
functions simply in order to be able to loop over them (this is whay I
was talking about 'abusing loop constructs'). Besides I am not happy
with the other two idioms I can think of.

Wouldn't it be easier to do:

if isinstance(x, int):
# do something
elif isinstance(x, float)t:
# do something
elif isinstance(x, complex):
# do something
else:
raise CantDoIt

or,

i = [int, float, complex]
for f in i:
if isinstance(x, f):
return x
else:
raise CantDoIt
 
G

garrickp

I'm not really thinking about this situation so let me clarify. Here
is a simple concrete example, taking the following for the functions
a,b,c I mention in my original post.
- a=int
- b=float
- c=complex
- x is a string
This means I want to convert x to an int if possible, otherwise a
float, otherwise a complex, otherwise raise CantDoIt.
I can do:
for f in int, float, complex:
try:
return f(x)
except ValueError:
continue
raise CantDoIt
But if the three things I want to do are not callable objects but
chunks of code this method is awkward because you have to create
functions simply in order to be able to loop over them (this is whay I
was talking about 'abusing loop constructs'). Besides I am not happy
with the other two idioms I can think of.

Wouldn't it be easier to do:

if isinstance(x, int):
# do something
elif isinstance(x, float)t:
# do something
elif isinstance(x, complex):
# do something
else:
raise CantDoIt

or,

i = [int, float, complex]
for f in i:
if isinstance(x, f):
return x
else:
raise CantDoIt

I so missed the point of this. Not my day. Please ignore my post.
 
G

Gabriel Genellina

En Wed, 07 Mar 2007 18:48:18 -0300, Arnaud Delobelle
for f in int, float, complex:
try:
return f(x)
except ValueError:
continue
raise CantDoIt

But if the three things I want to do are not callable objects but
chunks of code this method is awkward because you have to create
functions simply in order to be able to loop over them (this is whay I
was talking about 'abusing loop constructs'). Besides I am not happy
with the other two idioms I can think of.

Hmmm, functions are cheap - nobody is charging you $2 for each "def"
statement you write, I presume :)

A bit more serious, if those "chunks of code" are processing its input and
returning something that you further process... they *are* functions. If
you don't want them to be publicly available, use inner functions:

def xxxfactory(x):
def f1(x):
...
def f2(x):
...
def f3(x):
...

for f in f1,f2,f3:
try:
return f(x)
... same as above...
 
B

Bruno Desthuilliers

Arnaud Delobelle a écrit :
Hi all,

Imagine I have three functions a(x), b(x), c(x) that each return
something or raise an exception. Imagine I want to define a function
that returns a(x) if possible, otherwise b(x), otherwise c(x),
otherwise raise CantDoIt.

Here are three ways I can think of doing it:

----------
# This one looks ugly
Yes.

def nested_first(x):
try:
return a(x)
except:

<side-note>
Try avoiding bare except clauses. It's usually way better to specify the
type(s) of exception you're expecting to catch, and let other propagate.
try:
return b(x)
except:
try:
return c(x)
except:
raise CantDoIt

# This one looks long-winded

Yes. And not's very generic.
def flat_first(x):
try:
return a(x)
except:
pass
try:
return b(x)
except:
pass
try:
return c(x)
except:
raise CantDoIt

# This one only works because a,b,c are functions

It works with any callable. Anyway, what else would you use here ???
# Moreover it seems like an abuse of a loop construct to me

Why so ? loops are made for looping, adn functions are objects like any
other.
def rolled_first(x):
for f in a, b, c:
try:
return f(x)
except:
continue
raise CantDoIt

Here's an attempt at making it a bit more generic (<side-note>it still
lacks a way to specify which kind of exceptions should be silently
swallowed</side-note>.

def trythese(*functions):
def try_(*args, **kw):
for func in functions:
try:
return func(*args, **kw)
except: # FIX ME : bare except clause
pass
else:
# really can't do it, sorry
raise CantDoIt
return try_

result = trythese(a, b, c)(x)
----------
# This one isn't correct but looks the clearest to me
def wished_first(x):
try:
return a(x)
except:
return b(x)
except:
return c(x)
except:
raise CantDoIt

Having multiple except clauses is correct - but it has another semantic.

Note: I've chosen functions a, b, c, but really I'm looking for a way
that is suitable for any chunk of code.

A function is an object wrapping a chunk of code...

I personnaly find the loop-based approach quite clean and pythonic.
 
B

Bruno Desthuilliers

Gabriel Genellina a écrit :
En Wed, 07 Mar 2007 19:00:59 -0300, Bruno Desthuilliers



this kind of cose?
sorry
s/cose/code/

Ce genre de chose?
En quelques sortes, oui, quoique pas tout à fait !-)
 
D

Diez B. Roggisch

Arnaud said:
I'm not really thinking about this situation so let me clarify. Here
is a simple concrete example, taking the following for the functions
a,b,c I mention in my original post.
- a=int
- b=float
- c=complex
- x is a string
This means I want to convert x to an int if possible, otherwise a
float, otherwise a complex, otherwise raise CantDoIt.

I can do:

for f in int, float, complex:
try:
return f(x)
except ValueError:
continue
raise CantDoIt

But if the three things I want to do are not callable objects but
chunks of code this method is awkward because you have to create
functions simply in order to be able to loop over them (this is whay I
was talking about 'abusing loop constructs').

In your case, I don't consider it an abuse - au contraire. Because in
such a situation where a possibly growing number of functions dealing
with one value until one of them "fits" a loop is the natural thing to
do, as it won't change in appearance just because you add a new
conversion-function to some (semi-)global list. I'd consider it
especially good style in that case.

Diez
 
B

Bruno Desthuilliers

Arnaud Delobelle a écrit :
I'm not really thinking about this situation so let me clarify. Here
is a simple concrete example, taking the following for the functions
a,b,c I mention in my original post.
- a=int
- b=float
- c=complex
- x is a string
This means I want to convert x to an int if possible, otherwise a
float, otherwise a complex, otherwise raise CantDoIt.

I can do:

for f in int, float, complex:
try:
return f(x)
except ValueError:
continue
raise CantDoIt

But if the three things I want to do are not callable objects but
chunks of code this method is awkward because you have to create
functions

You have to write the "chunks of code" anyway, don't you ? So just
adding a def statement above each chunk is not such a big deal.

Remember that you can define nested funcs, that will close over the
namespace of the enclosing one, so you dont necessarily have to pollute
the global namespace nor explicitly pass the whole environment to each
of these functions.

Using the generic higher order func I proposed in a previous answer:

def maincode(tati, pouffin):
def a():
# some
# code
# here
# that may raise
return foo

def b():
# some
# shorter chunk
return bar

def c():
# yet
# some
# other
# code
return quux

return trythese(a, b)()

Is it really less readable than:

def maincode(tati, pouffin):
try:
# some
# code
# here
# that may raise
return foo

except_retry: # the missing(???) keyword you're after
# some
# shorter chunk
return bar

except_retry:
# yet
# some
# other
# code
return quux

else:
raise CantDoIt
 
B

Bruno Desthuilliers

Gabriel Genellina a écrit :
En Wed, 07 Mar 2007 18:48:18 -0300, Arnaud Delobelle



Hmmm, functions are cheap

To define. You pay the price when you call them !-)

(sorry, couldn't resist - I otherwise totally agree with you)
 
M

MonkeeSage

except_retry: # the missing(???) keyword you're after

What is 'except_retry'?

To the OP, with the loop and the callables you could also break out of
the loop when the condition is met and use the else condition to raise
the exception.

def test(arg):
for f in int, float, str, hex:
try:
return f(arg)
break # breaks on f==str
except:
pass
else:
raise ValueError # remove str above to see this
print test('^&%')

Regards,
Jordan
 
S

Steven D'Aprano

Hi all,

Imagine I have three functions a(x), b(x), c(x) that each return
something or raise an exception. Imagine I want to define a function
that returns a(x) if possible, otherwise b(x), otherwise c(x),
otherwise raise CantDoIt.

Here are three ways I can think of doing it:

----------
# This one looks ugly
def nested_first(x):
try:
return a(x)
except:
try:
return b(x)
except:
try:
return c(x)
except:
raise CantDoIt


Exceptions are great, but sometimes they get in the way. This is one of
those times.

NULL = object() # get a unique value
def result_or_special(func, x):
"""Returns the result of func(x) or NULL."""
try:
return func(x)
except Exception:
return NULL

def failer(x):
"""Always fail."""
raise CantDoIt

def function(x):
funcs = (a, b, c, failer)
for func in funcs:
result = func(x)
if result is not NULL: break
return result


Or if you prefer:

def function(x):
NULL = object()
funcs = (a, b, c)
for func in funcs:
try:
result = func(x)
except Exception:
pass
else:
break
else:
# we didn't break out of the loop
raise CantDoIt
# we did break out of the loop
return result
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top