Python Introduction to Exceptions

In the last lesson, we learned about Python Iterating Through Dictionaries. Now let’s understand what happens when Python runs into an error.

Let’s Talk About Things Going Wrong

We write a program. We test it. It works. We feel good.

Then someone else uses it. And they do something we did not expect. They type a letter when we asked for a number. They try to divide by zero. They open a file that does not exist.

And our program crashes. It stops. It shows a big scary error message. And the user has no idea what happened.

That is what exceptions are about. They are errors that happen while the program is running. And Python gives us a way to handle them. So our program does not crash. It can deal with the problem and continue.

What Is an Exception in Python?

An exception is an error that occurs during the execution of a program.

It is not a syntax error. Syntax errors happen before the program runs. They are like typos. Python catches them when it reads our code.

Exceptions happen while the program is running. Something unexpected happens. Like dividing by zero. Or accessing a list index that does not exist. Or opening a file that is not there.

When an exception happens, Python stops the normal flow. It creates an exception object. And it looks for someone to handle it. If nobody handles it, the program crashes.

Common Exceptions in Python

Let’s look at a few common exceptions. We have probably seen some already.

ZeroDivisionError

This happens when we divide by zero.

print(10 / 0)

Output

ZeroDivisionError: division by zero

ValueError

This happens when we try to convert something that cannot be converted.

number = int("hello")

Output

ValueError: invalid literal for int() with base 10: 'hello'

TypeError

This happens when we use the wrong type.

print("Age: " + 25)

Output

TypeError: can only concatenate str (not "int") to str

IndexError

This happens when we use an index that does not exist.

numbers = [1, 2, 3]
print(numbers[10])

Output

IndexError: list index out of range

KeyError

This happens when we use a dictionary key that does not exist.

person = {"name": "Alex"}
print(person["age"])

Output

KeyError: 'age'

FileNotFoundError

This happens when we try to open a file that does not exist.

file = open("missing.txt", "r")

Output

FileNotFoundError: [Errno 2] No such file or directory: 'missing.txt'

There are many more. But these are the ones we will see most often.

What Happens When an Exception Is Not Handled?

If an exception happens and nobody handles it, the program stops. It prints a traceback. That is the long error message that shows where the error happened.

print("Start")
print(10 / 0)
print("End")

Output

Start
Traceback (most recent call last):
File "example.py", line 2, in <module>
print(10 / 0)
ZeroDivisionError: division by zero

Notice that “End” is not printed. Because the program crashed at the division. It never reached the last line.

That is what happens without handling. The program just stops.

How to Handle Exceptions in Python

Python gives us a way to catch exceptions and handle them. So the program does not crash.

We use a try and except block.

try:
print(10 / 0)
except:
print("Something went wrong")

Output

Something went wrong

The code inside try is attempted. If an exception happens, Python jumps to the except block. It runs that code instead of crashing.

We will learn more about try and except in the next article. But that is the basic idea.

Python Exceptions: A Real-Life Example

Let’s say we are asking the user for a number. We want to divide 100 by that number.

number = int(input("Enter a number: "))
result = 100 / number
print(f"Result: {result}")

If the user enters 0, we get a ZeroDivisionError. If they enter hello, we get a ValueError. The program crashes.

With exception handling, we can deal with it.

try:
number = int(input("Enter a number: "))
result = 100 / number
print(f"Result: {result}")
except ValueError:
print("That is not a valid number")
except ZeroDivisionError:
print("Cannot divide by zero")

Now if the user enters 0, it says “Cannot divide by zero”. If they enter hello, it says “That is not a valid number”. No crash.

That is the power of exceptions.

Why Handle Exceptions in Python?

Three reasons.

First, it keeps our program running. Instead of crashing, it can deal with the problem and continue.

Second, it gives better error messages. Instead of a scary traceback, we can show the user a friendly message.

Third, it makes our code more robust. It can handle unexpected situations gracefully.

A Word of Caution

Let’s not overuse exceptions. They are for unexpected situations. Not for normal flow control.

For example, let’s not use exceptions to check if a key exists in a dictionary. We use in or get(). Exceptions should be for things that should not normally happen.

Common Mistakes with Python Exceptions

Mistake 1: Catching All Exceptions Blindly

try:
# some code
except:
pass

This catches every exception and does nothing. That is bad. We might hide a real bug. It is better to catch specific exceptions.

Mistake 2: Not Handling Exceptions at All

number = int(input("Enter a number: "))

If the user enters something wrong, the program crashes. Let’s always think about what could go wrong.

Mistake 3: Putting Too Much Code in try

try:
# 50 lines of code
except:
print("Error")

This makes it hard to know what went wrong. Let’s keep the try block small. Only the code that might raise the exception.

Mistake 4: Using Exceptions for Normal Flow

try:
value = my_dict["key"]
except KeyError:
value = "default"

This works. But it is slower and less clear than using get().

value = my_dict.get("key", "default")

Let’s use exceptions for exceptional cases.

Next, we will learn about Python try and except and handle errors without stopping the program.