Creating software is hard work. To make your software better, your application needs to keep working even when the unexpected happens. For example, let’s say your application needs to pull information down from the Internet. What happens if the person using your application loses their Internet connectivity?
Another common issue is what to do if the user enters invalid input. Or tries to open a file that your application doesn’t support.
All of these cases can be handled using Python’s built-in exception handling capabilities, which are commonly referred to as the try
and except
statements.
In this article you will learn about:
- Common exceptions
- Handling exceptions
- Raising exceptions
- Examining exception objects
- Using the
finally
statement - Using the
else
statement
Let’s get starting by learning about some of the most common exceptions.
The Most Common Exceptions
Python supports lots of different exceptions. Here is a shortlist of the ones that you are likely to see when you first begin using the language:
Exception
– The base exception that all the others are based onAttributeError
– Raised when an attribute reference or assignment fails.ImportError
– Raised when an import statement fails to find the module definition or when a from … import fails to find a name that is to be imported.ModuleNotFoundError
– A subclass of ImportError which is raised by import when a module could not be locatedIndexError
– Raised when a sequence subscript is out of range.KeyError
– Raised when a mapping (dictionary) key is not found in the set of existing keys.KeyboardInterrupt
– Raised when the user hits the interrupt key (normallyControl-C
orDelete
).NameError
– Raised when a local or global name is not found.OSError
– Raised when a function returns a system-related error.RuntimeError
– Raised when an error is detected that doesn’t fall in any of the other categories.SyntaxError
– Raised when the parser encounters a syntax error.TypeError
– Raised when an operation or function is applied to an object of inappropriate type. The associated value is a string giving details about the type mismatch.ValueError
– Raised when a built-in operation or function receives an argument that has the right type but an inappropriate value, and the situation is not described by a more precise exception such as IndexError.ZeroDivisionError
– Raised when the second argument of a division or modulo operation is zero.
For a full listing of the built-in exceptions, you can check out the Python documentation here:
Now let’s find out how you can actually handle an exception when one occurs.
Handling Exceptions
Python comes with a special syntax that you can use to catch an exception. It is known as the try/except
statement.
This is the basic form that you will use to catch an exception:
try: # Code that may raise an exception goes here except ImportError: # Code that is executed when an exception occurs
You put code that you expect might have an issue inside the try
block. This might be code that opens a file or code that gets input from the user. The second block is known as the except
block. This code will only get executed if an ImportError
is raised.
When you write the except
without specifying the exception type, it is known as a bare exception. These are not recommended:
try: with open('example.txt') as file_handler: for line in file_handler: print(line) except: print('An error occurred')
The reason it is bad practice to create a bare except is that you don’t know what types of exceptions you are catching. This can make figuring out what you did wrong more difficult. If you narrow the exception types down to the ones you expect, then the unexpected ones will actually make your application crash with a useful message.
At that point, you can decide if you want to catch that other condition or not.
Let’s say you want to catch multiple exceptions. Here is one way to do that:
try: with open('example.txt') as file_handler: for line in file_handler: print(line) import something except OSError: print('An error occurred') except ImportError: print('Unknown import!')
This exception handler will catch two types of exceptions: OSError
and ImportError
. If another type of exception occurs, this handler won’t catch it and your code will stop.
You can rewrite the code above to be a bit simpler by doing this:
try: with open('example.txt') as file_handler: for line in file_handler: print(line) import something except (OSError, ImportError): print('An error occurred')
Of course, by creating a tuple of exceptions, this will obfuscate which exception has occurred. In other words, this code makes it harder to know which exception occurred.
Raising Exceptions
What do you do after you catch an exception? You have a couple of options. You can print out a message like you have been in the previous examples. You could also log the message to a log file. Or if the exception is one that you know needs to stop the execution of your application, you can re-raise the exception.
Raising an exception is the process of forcing an exception to occur. You raise exceptions in special cases. For example, if the application gets into a bad state, you might raise an exception. You will also tend to raise exceptions after already handling the exception.
You can use Python’s built-in raise
statement to raise an exception:
try: raise ImportError except ImportError: print('Caught an ImportError')
When you raise an exception, you can have it print out a custom message:
>>> raise Exception('Something bad happened!') Traceback (most recent call last): Python Shell, prompt 1, line 1 builtins.Exception: Something bad happened!
If you don’t provide a message, then the exception would look like this:
>>> raise Exception Traceback (most recent call last): Python Shell, prompt 2, line 1 builtins.Exception:
Now let’s learn about the exception object!
Examining the Exception Object
When an exception occurs, Python will create an exception object. You can examine the exception object by assigning it to a variable using the as
statement:
>>> try: ... raise ImportError('Bad import') ... except ImportError as error: ... print(type(error)) ... print(error.args) ... print(error) ... <class 'ImportError'> ('Bad import',) Bad import
In this example, you assigned the ImportError
object to error
. Now you can use Python’s type()
function to learn what kind of exception it was. This would allow you to solve the issue mentioned earlier in this article when you have a tuple of exceptions but you can’t immediately know which exception you caught.
If you want to dive even deeper into debugging exceptions, you should look up Python’s traceback
module.
Using the finally
Statement
There is more to the try/except
statement than just try
and except
. You can add a finally
statement to it as well. The finally
statement is a block of code that will always get run even if there is an exception raised inside of the try
statement.
You can use the finally
statement for cleanup. For example, you might need to close a database connection or a file handle. To do that, you can wrap the code in a try/except/finally
statement.
Let’s look at a contrived example:
>>> try: ... 1 / 0 ... except ZeroDivisionError: ... print('You can not divide by zero!') ... finally: ... print('Cleaning up') ... You can not divide by zero! Cleaning up
This example demonstrates how you can handle the ZeroDivisionError
exception as well as add on clean up code.
But you can also skip the except
statement entirely and create a try/finally
instead:
>>> try: ... 1/0 ... finally: ... print('Cleaning up') ... Cleaning upTraceback (most recent call last): Python Shell, prompt 6, line 2 builtins.ZeroDivisionError: division by zero
This time you don’t handle the ZeroDivisionError
exception, but the finally
statement’s code block runs anyway.
Using the else
Statement
There is one other statement that you can use with Python’s exception handling and that is the else
statement. You can use the else
statement to execute code when there are no exceptions.
Here is an example:
>>> try: ... print('This is the try block') ... except IOError: ... print('An IOError has occurred') ... else: ... print('This is the else block') ... This is the try block This is the else block
In this code, no exception occurred, so the try
block and the else
blocks both run.
Let’s try raising an IOError
and see what happens:
>>> try: ... raise IOError ... print('This is the try block') ... except IOError: ... print('An IOError has occurred') ... else: ... print('This is the else block') ... An IOError has occurred
Since an exception was raised, only the try
and the except
blocks ran. Note that the try
block stopped running at the raise
statement. It never reached the print()
function at all. Once an exception is raised, all the following code is skipped over and you go straight to the exception handling code.
Wrapping Up
Now you know the basics of using Python’s built-in exception handling. In this article you learned about the following topics:
- Common exceptions
- Handling exceptions
- Raising exceptions
- Examining exception objects
- Using the
finally
statement - Using the
else
statement
Learning how to catch exceptions effectively takes practice. Once you have learned how to catch exceptions, you will be able to harden your code and make it work in a much nicer way even when the unexpected happens.
Related Reading
- Python 101: Conditional Statements
- Python 101: Learning About Loops
- Python 101: Learning About Sets
Pingback: Python 101: Exception Handling (Video) - Mouse Vs Python