Your first exception¶
In this tutorial, you will learn the fundamentals of exception handling in Python. You will write your first try/except block and learn how to handle common exceptions like ZeroDivisionError and FileNotFoundError.
Time commitment: 15–20 minutes
Prerequisites:
- Basic Python knowledge (variables, functions, and control flow)
- A Python 3.12 or later installation
Learning objectives¶
By the end of this tutorial, you will be able to:
- Explain what an exception is and why exceptions occur
- Use
try/exceptblocks to handle exceptions - Handle specific exception types such as
ZeroDivisionErrorandValueError - Print informative messages when exceptions occur
What is an exception?¶
An exception is an event that occurs during program execution and disrupts the normal flow of instructions. When Python encounters a situation it cannot handle, it raises an exception.
Let us see what happens when Python encounters an error.
# This will raise a ZeroDivisionError
# Uncomment the line below to see the exception:
# result = 10 / 0
If you uncomment and run the line above, Python will display a traceback — a detailed report showing where the error occurred and what type of exception was raised.
The traceback ends with a line like:
ZeroDivisionError: division by zero
This tells you two things:
- The type of exception:
ZeroDivisionError - A message describing the problem:
division by zero
Without exception handling, this error would cause your program to stop immediately. Let us learn how to handle it gracefully.
The try/except block¶
The try/except block is the foundation of exception handling in Python. It allows you to attempt an operation and specify what should happen if an exception occurs.
The basic structure is as follows:
try:
# Code that might raise an exception
except ExceptionType:
# Code that runs if the exception occurs
Let us try it with a division operation.
try:
result = 10 / 0
except ZeroDivisionError:
print("Cannot divide by zero.")
Excellent! Instead of crashing, the program printed a helpful message. Here is what happened:
- Python attempted to execute
10 / 0inside thetryblock - A
ZeroDivisionErrorwas raised - Python jumped to the
exceptblock and ran the code there - The program continued normally after the
try/exceptblock
Handling exceptions in functions¶
Exception handling is especially useful inside functions, where you want to deal with potential errors and return a meaningful result. Let us write a function that safely divides two numbers.
def safe_divide(a: float, b: float) -> float | None:
"""Safely divide two numbers, returning None if division by zero occurs."""
try:
return a / b
except ZeroDivisionError:
return None
print(safe_divide(10, 2)) # Normal division
print(safe_divide(10, 0)) # Division by zero
print(safe_divide(-6, 3)) # Negative numbers
The function returns None when division by zero occurs, rather than crashing. The caller can then check for None and respond appropriately.
Handling ValueError¶
A ValueError is raised when a function receives a value of the correct type but an inappropriate value. A common example is converting a non-numeric string to an integer.
def parse_integer(value: str) -> int | None:
"""Convert a string to an integer, returning None if conversion fails."""
try:
return int(value)
except ValueError:
return None
print(parse_integer("42")) # Valid integer string
print(parse_integer("hello")) # Not a number
print(parse_integer("3.14")) # Float string (not a valid integer)
Notice that "3.14" also returns None because int() does not accept strings containing decimal points.
Handling FileNotFoundError¶
When working with files, a FileNotFoundError is raised if you try to open a file that does not exist. This is one of the most common exceptions you will encounter.
def read_file_safely(filepath: str) -> str | None:
"""Read the contents of a file, returning None if the file is not found."""
try:
with open(filepath, "r", encoding="utf-8") as f:
return f.read()
except FileNotFoundError:
print(f"File not found: {filepath}")
return None
# This file does not exist, so the exception handler will run
content = read_file_safely("nonexistent_file.txt")
print(f"Result: {content}")
The function prints a helpful message and returns None instead of letting the program crash.
Accessing the exception object¶
You can access the exception object using the as keyword. This gives you access to the error message and other details.
try:
number = int("not_a_number")
except ValueError as e:
print(f"An exception occurred: {e}")
print(f"Exception type: {type(e).__name__}")
The variable e holds the exception object. You can convert it to a string to get the error message, or inspect its type using type().
What happens without exception handling?¶
To appreciate why exception handling matters, consider what happens when an exception is not handled. The program stops immediately, and any code after the error never runs.
def process_numbers(numbers: list[str]) -> list[int]:
"""Convert a list of strings to integers, skipping invalid values."""
results = []
for item in numbers:
try:
results.append(int(item))
except ValueError:
print(f"Skipping invalid value: {item!r}")
return results
data = ["10", "20", "abc", "30", "xyz", "40"]
valid_numbers = process_numbers(data)
print(f"Valid numbers: {valid_numbers}")
Without the try/except block, the function would have crashed on "abc" and never processed "30", "xyz", or "40". Exception handling allows the function to skip invalid values and continue processing.
import math
# Write your solution here
def safe_square_root(number: float) -> float | None:
"""Return the square root of a number, or None if the number is negative."""
pass # Replace this with your implementation
Click to reveal the solution
import math
def safe_square_root(number: float) -> float | None:
"""Return the square root of a number, or None if the number is negative."""
try:
return math.sqrt(number)
except ValueError:
return None
Exercise 2: Safe dictionary access¶
Write a function called get_value that takes a dictionary and a key. It should return the value for that key. If the key does not exist, handle the KeyError and return a default value of "unknown".
from typing import Any
# Write your solution here
def get_value(data: dict, key: str) -> Any:
"""Get a value from a dictionary, returning 'unknown' if the key is missing."""
pass # Replace this with your implementation
Click to reveal the solution
from typing import Any
def get_value(data: dict, key: str) -> Any:
"""Get a value from a dictionary, returning 'unknown' if the key is missing."""
try:
return data[key]
except KeyError:
return "unknown"
Summary¶
In this tutorial, you learned the following:
- An exception is an event that disrupts the normal flow of a program
- The
try/exceptblock lets you handle exceptions gracefully - You should handle specific exception types such as
ZeroDivisionError,ValueError, andFileNotFoundError - The
askeyword lets you access the exception object for more details - Exception handling allows your program to continue running when errors occur
In the next tutorial, Exception types, you will explore the built-in exception hierarchy and learn how Python organises its exceptions into a class hierarchy.