Chapter 3: Exception Handling
When writing programs, things don’t always go as planned. Unexpected situations can arise during the execution of a program, leading to errors. Python provides a robust mechanism called Exception Handling to deal with these situations gracefully.
3.1 What is an Exception?
An exception is an event that occurs during the execution of a program that disrupts the normal flow of instructions. When a Python script encounters a situation it cannot cope with, it “raises” an exception.
If the exception is not explicitly handled by the programmer, the program will abruptly terminate (crash) and display an error traceback.
Common Types of Errors
- Syntax Errors: Errors in the structure or grammar of the Python code (e.g., missing a colon
:or incorrect indentation). The program won’t even run. - Logical Errors: The program runs without crashing, but it produces incorrect results due to flawed logic.
- Runtime Errors (Exceptions): The statement is syntactically correct, but an error occurs during execution (e.g., trying to divide by zero, or trying to open a file that doesn’t exist). These are the errors we use exception handling for.
Examples of Built-in Exceptions:
ZeroDivisionError: Occurs when a number is divided by zero.NameError: Occurs when a variable name is not found (used before being defined).TypeError: Occurs when an operation or function is applied to an object of inappropriate type (e.g., adding a string to an integer).ValueError: Occurs when a function receives an argument of the correct type but an inappropriate value.IndexError: Occurs when trying to access an index that is out of range in a sequence (like a list or tuple).FileNotFoundError: Occurs when trying to access a file that does not exist.
3.2 Introduction to Exception Handling
The core concept behind exception handling is to anticipate potential errors and write code that says, “Try to do this, but if an error occurs, do this instead of crashing.”
In Python, this is achieved using the try, except, and finally blocks.
The try-except Block
tryblock: This block contains the code that you think might raise an exception.exceptblock: This block contains the code that will execute only if an exception occurs in the correspondingtryblock.
Syntax:
try:
# Code that may raise an exception
except ExceptionName:
# Code to handle the specific exception
Example 1: Handling Division by Zero Without exception handling:
# If y is 0, this program crashes immediately.
x = 10
y = 0
result = x / y
print(result)
With exception handling:
try:
x = 10
y = 0
result = x / y
print("Result:", result)
except ZeroDivisionError:
print("Error: Cannot divide by zero! Please provide a non-zero denominator.")
print("Program execution continues normally...")
Example 2: Handling Invalid Input (ValueError)
If you ask a user for an integer, but they type “hello”, it raises a ValueError.
try:
age = int(input("Enter your age: "))
print("You are", age, "years old.")
except ValueError:
print("Invalid input. Please enter a valid number.")
3.3 Handling Multiple Exceptions
A single try block can have multiple except blocks to handle different types of errors differently.
try:
numerator = int(input("Enter numerator: "))
denominator = int(input("Enter denominator: "))
result = numerator / denominator
print("Result:", result)
except ValueError:
print("Error: Please enter integer numbers only.")
except ZeroDivisionError:
print("Error: Denominator cannot be zero.")
except Exception as e:
# This acts as a catch-all for any other unexpected errors
print("An unexpected error occurred:", e)
(Note: While except Exception: catches almost everything, it’s generally best practice to catch specific, anticipated exceptions rather than using broad catch-alls.)
3.4 The finally Block
The finally block offers a place to put code that must be executed no matter what happens in the try and except blocks.
Whether an exception is raised or not, and whether an exception is handled or not, the code inside the finally block will always run. This is extremely useful for cleanup operations, such as closing files or releasing network/database connections.
Syntax:
try:
# Code that may cause an exception
except SomeException:
# Handle the exception
finally:
# Code that will ALWAYS execute
Example:
file_obj = None
try:
print("Attempting to open file...")
# This will raise FileNotFoundError if 'data.txt' doesn't exist
file_obj = open("data.txt", "r")
content = file_obj.read()
print("File read successfully.")
except FileNotFoundError:
print("Error: The file 'data.txt' was not found.")
finally:
print("Executing 'finally' block.")
# Always attempt to clean up
if file_obj:
file_obj.close()
print("File closed.")
Summary of the Flow:
- The
tryblock is executed. - If no exception occurs, the
exceptblock is skipped, thefinallyblock executes, and execution continues. - If an exception occurs, the rest of the
tryblock is skipped. If the exception type matches theexceptblock, theexceptblock is executed. Then, thefinallyblock executes. - If an exception occurs but is not handled by any
exceptblock, thefinallyblock is still executed. After that, the program halts with a traceback error.
Exception handling allows you to write robust, professional programs that handle errors gracefully, providing a better experience for the user and preventing catastrophic failures.