Try and except
The try and except syntax is used to catch and safely handle errors that are encountered during runtime. As a new developer, you'll eventually become accustomed to having people telling you that your scripts don't work. In Python, we use the try and except blocks to stop preventable errors from crashing our code. Please use the try and except blocks in moderation. Don't use them as if they were band-aids to plug up holes in a sinking ship—instead, reconsider your original design and contemplate modifying the logic to better prevent errors. One great way to help with this is to provide instructions for use through command-line arguments, documentation, or otherwise. Using these correctly will enhance the stability of your program. However, improper usage will not add any stability and can mask underlying issues in your code. A good practice is to use as few lines of code within a try and except block as possible; this way, the error handling is focused and addressed properly.
For example, say we have some code that performs a mathematical calculation on two numerical variables. If we anticipate that a user may accidentally enter non-integer or float values, we may want to wrap a try and except around the calculation to catch any TypeError exceptions that may arise. When we catch the error, we can try and convert the variables to integers with the class constructor method before entering the try and except block again. If successful, we have saved our code from a preventable crash and maintained specificity to prevent our program from accepting dictionary input, for example. In the case of receiving a dictionary object, we would want the script to crash and present debug information to the user.
Any line that has a reasonable chance of generating an error should be handled by its own try and except block with a solution for that specific line to ensure that we are properly handling the specific error. There are a few variations of the try and except block. In short, there are catch-all, catch-as-variable, and catch-specific types of blocks. The following pseudocode shows examples of how the blocks are formed:
# Basic try and except -- catch-all
try:
# Line(s) of code
except:
# Line(s) of error-handling code
# Catch-As-Variable
try:
# Line(s) of code
except TypeError as e:
print(e.message)
# Line(s) of error-handling code
# Catch-Specific
try:
# Line(s) of code
except ValueError:
# Line(s) of error-handling code for ValueError exceptions
The catch-all or bare except will catch any error. This is often regarded as a poor coding practice as it can lead to undesired program behaviors. Catching an exception as a variable is useful in a variety of situations. The error message of the exception stored in e can be printed or written to a log by calling e.message—this can be particularly useful when an error occurs within a large multi-module program. In addition, the built-in isinstance() function can be used to determine the type of error.
In the example that we'll look at next, we define two functions: give_error() and error_handler(). The give_error() function tries to append 5 to the my_list variable. This variable has not yet been instantiated and will generate a NameError instance. In the except clause, we are catching a base Exception and storing it in the e variable. We then pass this exception object to our error_handler() function, which we define later.
The error_handler() function takes an exception object as its input. It checks whether the error is an instance of NameError or TypeError, or it passes otherwise. Based on the type of exception, it will print out the exception type and error message:
>>> from __future__ import print_function
>>> def give_error():
... try:
... my_list.append(5)
... except Exception as e:
... error_handler(e)
...
>>> def error_handler(error):
... if isinstance(error, NameError):
... print('NameError:', error.message)
... elif isinstance(error, TypeError):
... print('TypeError:', error.message)
... else:
... pass
...
>>> give_error()
NameError: global name 'my_list' is not defined
Finally, the catch-specific try and except block can be used to catch individual exceptions and has targeted error-handling code for that specific error. A scenario that might require a catch-specific try and except block is working with an object, such as a list or dictionary, which may or may not be instantiated at that point in the program.
In the following example, the results list does not exist when it is called in the function. Fortunately, we wrapped the append operation in a try and except to catch the NameError exceptions. When we catch this exception, we first instantiate the results list as an empty list and then append the appropriate data before returning the list. Here is the example:
>>> def double_data(data):
... for x in data:
... double_data = x*2
... try:
... # The results list does not exist the first time
... # we try to append to it
... results.append(double_data)
... except NameError:
... results = []
... results.append(double_data)
... return results
...
>>> my_results = doubleData(['a', 'b', 'c'])
>>> print my_results
['aa', 'bb', 'cc']