Python Try Except: Exceptions Tutorial With Example Code

Python exception handling is the process of identifying and responding to errors in a program. In other words, it is a way to deal with errors that might occur in your program. In this article, you will learn how to handle errors in Python by using the Python try and except keywords. You’ll also learn how to create custom exceptions, which can be used to define your own specific error messages.

What is an exception?

An exception is a condition that arises during the execution of a program. It is a signal that something unexpected happened. Python represents exceptions by an object of a certain type.

In Python, all built-in, non-system-exiting exceptions are derived from the Exception class. Exceptions have their own, descriptive names. For example, if you try to divide a number by zero, you will get a ZeroDivisionError exception, which is also a subclass of the Exception class.

You can view a complete hierarchy of all exceptions in the Python manual if you’re interested. Here’s a small excerpt from this hierarchy, just to illustrate:

BaseException
 +-- SystemExit
 +-- KeyboardInterrupt
 +-- Exception
      +-- ArithmeticError
      |    +-- FloatingPointError
      |    +-- OverflowError
      |    +-- ZeroDivisionError
      .....

In case your knowledge about objects, classes, and inheritance is a bit rusty, you may want to read my article on objects and classes and my article on inheritance first.

Python try except

When something unexpected occurs, we can raise an exception at the point of the error. When an exception is raised, Python stops the current flow of execution and starts looking for an exception handler that can handle it. So what is an exception handler? Here’s where the try and except statements come into play.

python try except
try.. except.. else.. finally

As demonstrated in the illustration, we can create a block of code by starting with a try statement. This basically means: try to run this code, but an exception might occur.

After our try block, one or more except blocks must follow. This is where the magic happens. These except blocks can catch an exception, as we usually call this. In fact, many other programming languages use a statement called catch instead of except. Each except block can handle a specific type of exception.

Remember: classes are hierarchical. For that reason, exceptions are too. Hence, except blocks must go from most specific, like a ZeroDivisionError, to less specific, like an ArithmeticError.

To demonstrate this, imagine what happens when we would start with an except block that catches Exception. This first block would catch basically everything, because most exceptions inherit from this one, rendering the other except blocks useless.

Now let’s first go back to raising an exception. When an exception is raised, the exception handler that’s able to handle the exception can be nearby, but it can also be in a different module. What’s important to realize, is that Python won’t just scan your code at random for an exception handler. Instead, the handler should be somewhere in the call stack.

Please forget about else and finally for now. I’ll explain them in detail further down in this article. We first need to discuss call stacks, to truly understand how an exception finds its way to an exception handler.

Call stack

A call stack is an ordered list of functions that are currently being executed. For example, you might call function A, which calls function B, which calls function C. We now have a call stack consisting of A, B, and C. When C raises an exception, Python will look for an exception handler in this call stack, going backward from end to start. It can be in function C (closest to the exception), in function B (somewhat farther), in function A, or even at the top level of the program where we called function A.

If Python finds a suitable except block, it executes the code in that block. If it doesn’t find one, Python handles the exception by itself. This means it will print the exception and exit the program since it has no clue what to do with it.

I hope you’re still with me! If not, no worries. The examples on this page will hopefully make all this more clear. You might want to revisit this section once you’ve finished the entire article.

Catching exceptions with try except

Let’s finally write some actual code! To handle an exception, we need to catch it. As we just learned, we can catch an exception by using the try and except keywords. When an exception occurs while we are inside the try block, the code in the except block is executed.

A simple example

Let’s try a simple example first. As you hopefully know, we can’t divide by the number zero. If we do so anyway, Python will throw and exception called ZeroDivisionError, which is a subclass of ArithmeticError:

try:
    print(2/0)
except ZeroDivisionError:
    print("You can't divide by zero!")

If you call a Python function inside the try block, and an exception occurs in that function, the flow of code execution stops at the point of the exception and the code in the except block is executed. Try doing this again, without try and except. You’ll see that Python prints the exception for us. You can do so in the following code crumb:

Also, note that Python prints the error to stderr if you don’t handle the exception by yourself. In the crumb above, this is visible because the output appears in an ‘Error’ tab instead of an ‘Output’ tab.

Catching IOError

Let’s try another, more common example. After all, who divides a number by zero, right?

Exceptions are likely to occur when interacting with the outside world, e.g. when working with files or networks. For example, if you try to open a file with Python, but that file doesn’t exist, you will get an IOError exception. If you don’t have access to a file due to permissions, you will again get an IOError exception. Let’s see how to handle these exceptions.

Assignment

Please do the following:

  1. Run the code below, notice the file name (it doesn’t exist). See what happens.
  2. Alter the file name to myfile.txt file and run the code again. What happens now?
Python try-except assignment

Alternatively, here’s the code to copy/paste:

try:
    # Open file in read-only mode
    with open("not_here.txt", 'r') as f:
        f.write("Hello World!")
except IOError as e:
    print("An error occurred:", e)

Answers

In the first case, the file is not found. You should get this output:

An error occurred: [Errno 2] No such file or directory: 'not_here.txt'

In the second case, you’ll still get an error after creating the file. This time because we’re trying to write to a file that is opened in read-only mode. For more information on these modes, read the article on opening, reading, and writing files with Python. The error should look like this:

An error occurred: not writable

Although this is an error, it’s not written to the stderr output of the operating system. That’s because we handled the exception ourselves. If you removed the try.. except from the code completely and then try to write to the file in read only mode, Python will catch the error, force the program to terminate, and show this message:

Traceback (most recent call last):
  File "tryexcept.py", line 3, in <module>
    f.write("Hello World!")
io.UnsupportedOperation: not writable

The finally and else blocks

Remember the other two blocks that I asked you to forget for a while? Let’s look at those now, starting with the finally block.

The finally block in try-except

The finally block is executed regardless of whether an exception occurs or not. Finally blocks are useful, for example, when you want to close a file or a network connection regardless of what happens. After all, you want to clean up resources to prevent memory leaks.

Here’s an example of this at work, in which we open a file without using the with statement, forcing us to close it ourselves:

try:
    # Open file in write-mode
    f = open("myfile.txt", 'w')
    f.write("Hello World!")
except IOError as e:
    print("An error occurred:", e)
finally:
    print("Closing the file now")
    f.close()

You can also try this interactive example:

You should see the ‘Closing the file now’ message printed on your screen. Now change the writing mode to ‘r’ instead of ‘w’. You’ll get an error since the file does not exist. Despite this exception, we try to close the file anyway thanks to the finally block. This in turn goes wrong too: a NameError exception is thrown because the file was never opened, and hence f does not exist. You can fix this with a nested try.. except NameError. Try it for yourself.

The else block in try-except

In addition to the except and finally blocks, you can add an else block. The else block is executed only when no exception occurs. So it differs from the finally block since finally is executed even if an exception occurs.

When should you use the else block? And why shouldn’t you just add extra code to the try block? Good questions!

According to the Python manual, the use of the else clause is better than adding additional code to the try clause. But why? The reasoning is that it avoids accidentally catching an exception that wasn’t raised by the code being protected by the try and except statements in the first place. I admit I don’t use else blocks very often myself. Also, I find them somewhat confusing, especially for people coming from other languages.

Common Python exceptions

Some exceptions are so common that you’ll inevitably encounter them. Here are a few of the most common ones:

Exception nameWhen you’ll encounter itExample situation that raises the exception
SyntaxErrorRaised when there is an error in Python syntax. If not caught, this exception will cause the Python interpreter to exit.pritn('test')
KeyErrorRaised when a key is not found in a dictionary.d = { 'a': 1}
d['b']
IndexErrorRaised when an index is out of range.lst = [1, 2, 3]
lst[10]
KeyboardInterruptRaised when the user hits the interrupt key (Ctrl+C)Pressing control+c
Some common exceptions you will encounter at some point in your carreer

If you like, you can try to evoke these exceptions intentionally. I’ll promise you, you will encounter these countless times in your Python programming career. Knowing and understanding what they mean and when they occur will greatly help you debug your code.

Exception best practices

Now that we know the mechanics of handling exceptions, there are a couple of best practices I’d like to share with you.

Don’t use blank except blocks

I’ve written about this before in the blog post ‘How not to handle exceptions in Python‘. When you want to catch a broad range of exceptions, don’t use a blank except block. By this, I mean something like:

try:
    ...
except:
    print("An error occurred:")

You might encounter this in code samples on the web. If you do, make a habit of improving the exception handling. Why should you, and how can you improve code like the example above?

All exceptions, including system exceptions, inherit from a class called BaseException. If an except clause mentions a particular class, that clause also handles any exception classes derived from that class. An empty except is equivalent to except BaseException, hence it will catch all possible exceptions.

So although the syntax is allowed, I don’t recommend it. E.g., you’ll also catch KeyboardInterrupt and SystemExit exceptions, which prevent your program from exiting. Instead, use a try block with a list of explicit exceptions that you know you can handle. Or, if you really need to, catch the Exception base class to handle almost all the regular exceptions, but not the system ones.

If you’re feeling adventurous, you can try to catch all exceptions and see what happens:

from time import sleep

while True:
    try:
        print("Try and stop me")
        sleep(1)
    except:
        print("Don't stop me now, I'm having such a good time!")

You’ll probably need to close your terminal to stop this program. Now change the except block to catch Exception. You will still catch almost all exceptions, but the program will exit on system exceptions like KeyboardInterrupt and SystemExit:

from time import sleep

while True:
    try:
        print("Try and stop me")
        sleep(1)
    except Exception:
        print("Something went wrong")

It’s better to ask for forgiveness

In Python, you’ll often see a pattern where people simply try if something works, and if it doesn’t, catch the exception. In other words, it’s better to ask for forgiveness than permission. This is in contrast to other languages, where you preferably ask for permission. E.g., in Java, exceptions can slow down your program and you “ask for permission” by doing checks on an object instead of simply trying.

To make this more concrete: in Python, we often just try to access the key in a dictionary. If the key doesn’t exist, we’ll get an exception and handle it. Suppose we just converted some externally provided JSON to a dictionary, and now start to use it:

import json

user_json = '{"name": "John", "age": 39}'
user = json.loads(user_json)

try:
    print(user['name'])
    print(user['age'])
    print(user['address'])
    ...
except KeyError as e:
    print("There are missing fields in the user object: ", e)
    # Properly handle the error
    ...

This will print the error:

There are missing fields in the user object: 'address'

We could have added three checks (if 'name' in user, if 'age' in user, etc.) to make sure that all the fields are there. But this is not a good practice. It potentially introduces a lot of code just to check if keys exist. Instead, we ask for forgiveness in our except block once, which is much cleaner and more readable. And if you’re worried about performance, exceptions don’t take up that many CPU cycles in Python. Lots of comparisons are in fact slower than catching a single exception (if it occurs at all!).

Create custom exceptions

As we learned before, all built-in, non-system-exiting exceptions are derived from the Exception class. All user-defined exceptions should also be derived from this class. So if we want to create our own exceptions, we need to create a subclass of the Exception class.

For example, if you want to create an exception that indicates that a user was not found, you can create a UserNotFoundError exception. This would, in its most basic form, look like this:

class UserNotFoundError(Exception):
    pass

This inherits all the properties and methods of Exception, but we give it a new name to distinguish it from the Exception class. This way, we’ll be able to specifically catch it with an except block. We’ll use this class in the example that follows.

Raising (or throwing) exceptions

We know some built-in exceptions and how to create custom exceptions. We also know how to catch exceptions with try and except. What’s left, is what’s called raising or throwing an exception. You can raise an exception yourself with the raise keyword.

In the example below, we use your previously defined UserNotFoundError. We call a function, fetch_user, that fetches some user data from an imaginary database. If the user is not found, this database returns None. We decided that we don’t want to return None, which would force the caller to check for None every time. Instead, we use our custom UserNotFoundError.

class UserNotFoundError(Exception):
    pass

def fetch_user(user_id):
    # Here you would fetch from some kind of db, e.g.:
    # user = db.get_user(user_id)

    # To make this example runnable, let's set it to None
    user = None

    if user == None:
        raise UserNotFoundError(f'User {user_id} not in database')
    else:
        return user

users = [123, 456, 789]
for user_id in users:
    try:
        fetch_user(user_id)
    except UserNotFoundError as e:
        print("There was an error: ", e)

Assignment

Here’s a little assignment. We could have used a regular Exception object instead. That way, we don’t need to define a custom one. Why is this a bad idea?

Answer

You can in fact raise a regular exception, e.g. with raise Exception('User not found'). But if you do, you need to catch all exceptions of type Exception. And as we know, there are a lot of those. Chances are you inadvertently catch some other exception that you’re not able to handle. For example, the database client might throw a DatabaseAuthenticationError which is also a subclass of Exception.

How to print a Python exception

You can print exceptions directly as long as you catch them properly. You may have seen examples of this above already. To be clear, here’s an example of how to catch and print an exception:

try:
    ...
except Exception as e:
    print("There was an error: ", e)

If you’d like to print the call stack, just like Python does when you don’t catch the exception yourself, you can import the traceback module:

import traceback

try:
    ...
except Exception:
    traceback.print_exc()

Keep learning

Here are some more resources to deepen your knowledge:

About Erik van Baaren

Erik is the owner of Python Land and the author of many of the articles and tutorials on this website. He's been working as a professional software developer for 25 years, and he holds a Master of Science degree in computer science. His favorite language of choice: Python! Writing good articles takes time and effort. Did you like this tutorial? You can buy him a coffee to show your appreciation.

Subscribe
Notify of
2 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments