Skip to main content

Section 12.4 Principles for using Exceptions

There are many bad examples of exception use on the Internet. The purpose of an exception is to modify the flow-of-control, not to catch simple errors.

Subsection 12.4.1 Principle 1:

If a condition can be handled using the normal flow-of-control, don’t use an exception!
Example 1:
Don’t use ZeroDivisionError to test for an empty list:
try:
  average = sum(a_list) / len(a_list)
except ZeroDivisionError:
  average = 0
When you can just as easily test for no items in the list doing this:
if len(a_list) > 0:
  average = sum(a_list) / len(a_list)
else:
  average = 0
Example 2:
Don’t use IndexError to test for a valid list index:
try:
  value = my_list[index]
except IndexError:
  value = -1
When you can just as easily test for a valid index doing this:
if 0 <= index < len(my_list):
  value = my_list[index]
else:
  value = -1
Example 3:
Don’t use KeyError to test if a key is in a dictionary:
try:
  value = my_dictionary[key]
except KeyError:
  value = -1
When you can just as easily test to see if the key is valid doing this:
if key in my_dictionary.keys():
  value = my_dictionary[key]
else:
  value = -1

Subsection 12.4.2 Principle 2:

If you call a function that potentially raises exceptions, and you can do something appropriate to deal with the exception, then surround the code that contains the function call with a try: except: block.
Example: Suppose you have a function that reads a file to set the state of an application when it starts up. You should catch any errors related to reading the file and set the state of the application to default values if they can’t be set from the file.
try:
  load_state('previous_state.txt')
except OSError:
  set_state_to_defaults()

Subsection 12.4.3 Principle 3:

If you call a function that potentially raises exceptions, and you can’t do anything meaningful about the conditions that are raised, then don’t catch the exception message(s).