Reputation: 1587
In a Python program, one generally catches an exception using a try-except block:
try:
# Do stuff
except ValueError:
# Handle exception
The best way I know of to catch an exception in an exception handler is a nested try-except block. However, with many nested try-catch blocks, this may get a bit messy:
try:
# Some assignment that throws an exception if the object is not in the list
try:
# Some assignment function that throws an exception if the the object is not already in the database
# Error Handling
except ValueError:
try:
# Some function that throws an exception if the object does not have an undesired property
# Error Handling
except AttributeError:
try:
# Some function that throws an exception if an error happens
except Exception:
# Exception handling
except ValueError:
# Exception handling
Is there a neater way to do this? Something like:
try:
# Some assignment that throws an exception if the object is not in the list
try:
# Some assignment function that throws an exception if the object is not already in the database
except ValueError:
# Some function that throws an exception if the object does not have an undesired property
exceptexcept AttributeError:
# Some function that throws an exception if an error happens
exceptexcept Exception:
# Exception handling
except ValueError:
# Exception handling
Upvotes: 2
Views: 5411
Reputation: 18625
This sounds like a loop where you want to keep trying until you succeed or run out of options. So you could implement it that way, e.g., something like this
# Each pair contains a function to call and an exception that can be caught.
# If that exception is raised, the next function will be tried.
action_list = [
(get_from_list, ValueError), # throws ValueError if item can't be retrieved
(get_from_database, ValueError), # throws ValueError if item can't be retrieved
(get_from_object, AttributeError), # throws AttributeError if item lacks desired property
]
result = None
for action, ex in action_list:
try:
result = action(key)
break
except ex:
continue
You could tidy this up a bit by having all your helper functions raise a custom exception like "NotFound", which you then use as a signal to check the next level, like this:
# actions to try; all raise NotFound if unsuccessful
action_list = [
get_from_list, get_from_database, get_from_object
]
result = None
for action in action_list:
try:
result = action(key)
break
except NotFound:
continue
Or you could put all the steps in a function that returns as soon as it succeeds. This way your assignments could be done in regular code rather than using helper functions:
def get_value(key):
try:
return some_list[int(key)]
except ValueError:
pass
try:
return get_from_database(key)
except ValueError:
pass
try:
return getattr(some_object, key)
except AttributeError:
pass
return None
If you don't want another function you could abuse a for
loop:
result = None
for _ in range(1):
try:
result = some_list[int(key)]
break
except ValueError:
pass
try:
result = get_from_database(key)
break
except ValueError:
pass
try:
result = getattr(some_object, key)
break
except AttributeError:
pass
Or you could use a single outer try
/except
with a custom Found
exception as a "structured goto":
result = None
try:
try:
result = some_list[int(key)]
raise Found
except ValueError:
pass
try:
result = get_from_database(key)
raise Found
except ValueError:
pass
try:
result = getattr(some_object, key)
raise Found
except AttributeError:
pass
except Found:
pass # all good
Or there's this tried-but-true construct:
result = None
if result is None:
try:
result = some_list[int(key)]
except ValueError:
pass
if result is None:
try:
result = get_from_database(key)
except ValueError:
pass
if result is None:
try:
result = getattr(some_object, key)
except AttributeError:
pass
Upvotes: 5