This chapter is critical for writing robust, professional code. We’ll detail how to anticipate and manage errors gracefully using the try, except, else, and finally blocks, preventing your program from crashing when unexpected situations occur.
1. Errors vs. Exceptions
Understanding the two main categories of issues in Python is essential:
- Syntax Errors (or Parsing Errors): These occur when you violate Python’s grammar rules (e.g., forgetting a colon
:or mis-indenting code). The interpreter cannot run the code at all. - Exceptions (Runtime Errors): These occur when the syntax is correct, but an error arises during execution (e.g., trying to divide by zero, accessing a dictionary key that doesn’t exist). These can be handled.
2. Common Built-in Exceptions
Python defines many types of exceptions. Handling the specific type of error gives you precise control over your program’s response.
| Exception | Cause | Example |
ZeroDivisionError | Division or modulo operation by zero. | 1 / 0 |
TypeError | Operation applied to an incompatible type. | '5' + 1 |
NameError | Attempting to use a variable name that hasn’t been defined. | print(undefined_var) |
IndexError | Trying to access an index that is out of range for a list or tuple. | my_list[10] |
KeyError | Trying to access a non-existent key in a dictionary. | my_dict['missing'] |
ValueError | A function receives an argument of the correct type but an inappropriate value. | int('hello') |
3. The try and except Blocks
The core of error handling is the try...except structure.
try: The code that might raise an exception is placed inside this block.except: If an exception occurs in thetryblock, the execution immediately stops there, and the code inside theexceptblock is run.
A. Catching Specific Exceptions
Catching a specific exception type is best practice, as it prevents your handler from hiding unrelated bugs.
try:
num1 = int(input("Enter a number: "))
result = 10 / num1
print(f"Result: {result}")
# Handles only the case where input cannot be converted to int
except ValueError:
print("Error: Invalid input. Please enter a valid whole number.")
# Handles only the case where the user entered zero
except ZeroDivisionError:
print("Error: Cannot divide by zero.")
# Handles any other unforeseen exception
except Exception as e:
print(f"An unexpected error occurred: {e}")
B. Catching the Exception Object
The optional as e syntax captures the exception object itself, allowing you to print or log the specific error message provided by Python.
4. The else and finally Blocks
These optional blocks provide even more control over the post-exception flow.
| Block | Purpose | Execution Timing |
else | Code to run if the try block succeeds (no exceptions were raised). | Runs only if try completes successfully. |
finally | Code that must be executed regardless of whether an exception occurred or was handled. | Always runs, often used for cleanup (e.g., closing a database connection). |
try:
file = open("temp_log.txt", "w")
file.write("Attempting write...")
# 1. This code runs first
except IOError:
# 3a. Runs if file handling error occurs
print("An I/O error occurred.")
else:
# 3b. Runs if NO exception occurred in try block
print("File operation successful.")
finally:
# 4. ALWAYS runs, ensuring cleanup
file.close()
print("Resource closed, regardless of outcome.")
5. Raising Exceptions
You can intentionally trigger an exception using the raise statement to signal that a specific condition or business rule has been violated.
def check_age(age):
if age < 0:
# Intentionally stop execution and signal an error
raise ValueError("Age cannot be a negative number.")
return age
try:
check_age(-5)
except ValueError as e:
print(f"Handled custom error: {e}")
