Exception Handling in Python
A program asks the user for their age and adds a year:
Python 3.13age = int(input("How old are you? ")) print("Next year you'll be", age + 1)
As long as they type a number, everything works. But the moment the user types "forty" instead of 40, int() can't turn it into a number: the program crashes with a ValueError, and the print line never runs. One bad input takes down the whole program.
Exception handling is for exactly these moments: catch the error, respond to it, and keep going, instead of crashing at the first surprise.
What are exceptions?
Try-except blocks
The main mechanism for exception handling in Python is the try-except block. It allows you to:
- Isolate potentially dangerous code
- Catch errors
- Perform alternative actions
- Continue program execution
Try-except block syntax
Python 3.13try: # Risky code that might raise an exception except ExceptionType: # Code that will execute when the specified exception occurs
Here's a simple example:
Python 3.13try: # Code that might raise an exception result = 10 / 0 # Division by zero will raise ZeroDivisionError except ZeroDivisionError: # Code that will execute when the exception occurs print("Error: division by zero!")Error: division by zero!
Complete try-except block structure
The complete structure of a try-except block includes additional sections:
Python 3.13try: # Risky code except ExceptionType1: # Handle exception type 1 except ExceptionType2: # Handle exception type 2 else: # Executes if no exceptions occurred in the try block finally: # Always executes, regardless of whether exceptions occurred
Handling different types of exceptions
Sequential handling of different exceptions
You can handle different types of exceptions differently:
Python 3.13try: # Several operations that might raise different exceptions file_name = input("Enter file name: ") # Let's assume "data.txt" was entered file = open(file_name, "r") line = file.readline() number = int(line.strip()) result = 100 / number print(f"Result: {result}") except FileNotFoundError: print(f"File {file_name} not found") except ValueError: print("Cannot convert data to a number") except ZeroDivisionError: print("Error: division by zero") except Exception as e: print(f"An unknown error occurred: {e}")File data.txt not found
Important: Python checks the except blocks in the order they are declared and executes the first matching block.
Handling multiple exceptions with one block
If you need the same handling for different exceptions:
Python 3.13try: value = int("abc") result = 10 / 0 except (ValueError, ZeroDivisionError): print("A calculation error occurred")A calculation error occurred
Else and finally blocks
Else block
The else block executes only if no exceptions were raised in the try block:
Python 3.13try: number = int("42") # This will execute successfully except ValueError: print("This is not a number") else: # This block will execute since no exception occurred print(f"Success! Number: {number}") print(f"Square of the number: {number ** 2}")Success! Number: 42 Square of the number: 1764
The else block is useful when you need to execute code only after a risky operation has completed successfully.
Finally block
The finally block always executes, regardless of whether an exception occurred or not:
Python 3.13# open() is outside try: if the file didn't open, there's nothing to close f = open("example.txt", "w") try: f.write("Hello, world!") # an exception might occur here finally: f.close() # runs even if the write above fails print("File closed")File closed
Note that open() sits before the try. If you put it inside and the file fails to open, the variable f simply won't exist, and f.close() in finally would crash by itself. This is exactly the fuss the with statement from the files chapter automates — it closes the file for you.
The finally block is typically used for:
- Releasing resources (closing files, database connections)
- Cleaning up temporary data
- Logging the completion of an operation
Getting information about an exception
Use as to get the exception object:
Python 3.13try: result = 10 / 0 except ZeroDivisionError as e: print(f"Exception type: {type(e).__name__}") print(f"Message: {e}")Exception type: ZeroDivisionError Message: division by zero
Raising exceptions
You can raise exceptions yourself using the raise statement:
Python 3.13def check_age(age): if age < 0: raise ValueError("Age cannot be negative") if age < 18: print("You are a minor") else: print("You are an adult") try: check_age(-5) except ValueError as e: print(f"Error: {e}")Error: Age cannot be negative
Creating custom exceptions
You can create your own exception classes by inheriting from standard ones:
Python 3.13# Creating a custom exception class class InvalidEmailError(Exception): """Raised when an email doesn't match the format""" pass def validate_email(email): if "@" not in email: raise InvalidEmailError("Email must contain the @ symbol") print(f"Email {email} is valid") try: validate_email("user.example.com") except InvalidEmailError as e: print(f"Validation error: {e}")Validation error: Email must contain the @ symbol
Practical recommendations
1. Specify concrete exceptions
Python 3.13# Bad: try: number = int("abc") except: # Catches all exceptions print("Error") # Good: try: number = int("abc") except ValueError: print("Invalid number format")Invalid number format
2. Minimize code in the try block
Python 3.13# Good: try: file = open("data.txt", "r") except FileNotFoundError: print("File not found") file = None if file: try: content = file.read() except: print("Error reading file") finally: file.close()File not found
3. Use else and finally correctly
Python 3.13def get_value_from_list(my_list, index): try: value = my_list[index] except IndexError: print(f"Index {index} out of range") return None else: # This code executes only if no exception occurred print(f"Value successfully retrieved") return value finally: # This code always executes print("List access operation completed") result = get_value_from_list([1, 2, 3], 1) print(f"Result: {result}")Value successfully retrieved List access operation completed Result: 2result = get_value_from_list([1, 2, 3], 10) print(f"Result: {result}")Index 10 out of range List access operation completed Result: None
Understanding check
Which block always executes, regardless of whether an exception occurred or not?
