Chapter 13: Handling the Unexpected - Errors and Exceptions

As you write more complex programs, you'll inevitably encounter errors. This is a completely normal part of programming! Sometimes these are simple syntax errors that Python points out before the program even runs. But other times, errors happen while the program is running, causing it to stop and crash. These are called exceptions.

For example, what happens if your program tries to:

  • Read a file that doesn't exist? (FileNotFoundError)

  • Divide a number by zero? (ZeroDivisionError)

  • Convert a word like "hello" into an integer? (ValueError)

In these cases, Python "raises an exception" and halts. In this chapter, you'll learn how to anticipate and manage these exceptions gracefully, preventing your programs from crashing and making them more robust and user-friendly.

The Problem: An Unhandled Exception

Let's look at a simple program that can easily crash.

# calculator.py
age_str = input("What is your age? ")
age = int(age_str) # This line can cause a ValueError

result = 100 / age # This line can cause a ZeroDivisionError

print(f"100 divided by your age is: {result}")

If you run this and enter a number like 25, it works perfectly. But if you enter zero or 0, you get a crash with a wall of red text ending in ZeroDivisionError. If you enter ten, you get a ValueError. This red text is called a traceback, and it's Python's way of telling you what went wrong and where.

A traceback is helpful for you, the developer, but it's a confusing and abrupt end for a user. We can do better.

The Solution: The try...except Block

To handle potential exceptions, you can place your "risky" code inside a try block. If an error occurs in that block, the code inside the corresponding except block is executed, and the program doesn't crash.

Here's the structure:

Let's make our calculator safer.

Now, if you enter ten or 0, the program prints our friendly error message and continues on instead of crashing.

Being Specific: Handling Different Exceptions

The except block in the example above is a bit too general—it catches every possible error. It's better practice to be specific about the exceptions you expect to handle. This way, you don't accidentally hide bugs you weren't aware of.

You can specify the type of exception you want to "catch".

Now our program gives a more specific, helpful message depending on what went wrong.

The else and finally Blocks

There are two more optional parts of a try...except statement that give you even more control.

The else Block

The code in the else block runs only if the try block completes successfully (i.e., no exceptions were raised). This is useful for code that depends on the try block succeeding.

The finally Block

The code in the finally block runs no matter what. It will execute whether the try block succeeded, failed with an exception, or even if you exit the program from within the try block. It's often used for cleanup actions, like closing a file.

Summary and What's Next

You've learned how to make your programs much more resilient.

  • An exception is an error that happens during program execution.

  • The try block contains code that might raise an exception.

  • The except block contains the code to handle that exception. It's best to catch specific exceptions like ValueError or FileNotFoundError.

  • The else block runs only if no exception occurred.

  • The finally block runs no matter what.

This is a key concept in writing professional, high-quality code. Now that you can handle data, files, and errors, you're ready to learn about a powerful new way to structure your programs. In the next chapter, we'll introduce the foundational concepts of Object-Oriented Programming (OOP), which will change the way you think about writing code.

Practice Time!

  1. Take the guest_book.py program from the last chapter. It already uses try...except FileNotFoundError. Add a finally block that prints "Guest book interaction complete."

  2. Write a program that has a list of numbers [1, 5, 0, 8]. Loop through the list and for each number n, try to print the result of 10 / n. Use try...except ZeroDivisionError to handle the case where n is 0.

Last updated