Python, like any other programming language, employs exceptions to handle runtime errors gracefully. Understanding and effectively using exceptions is crucial for writing clean, robust, and maintainable Python code. This post looks into the world of Python exceptions, providing clear explanations and practical code examples.
What are Exceptions?
Exceptions are events that disrupt the normal flow of a program’s execution. They occur when something unexpected happens, such as attempting to open a non-existent file, dividing by zero, or accessing an invalid index in a list. Without exception handling, these errors would typically crash your program.
Common Exception Types
Python offers a wide range of built-in exceptions. Here are some of the most frequently encountered:
ZeroDivisionError
: Raised when dividing by zero.TypeError
: Raised when an operation is performed on an object of an inappropriate type.NameError
: Raised when a variable is used before it has been assigned a value.FileNotFoundError
: Raised when trying to open a file that doesn’t exist.IndexError
: Raised when trying to access an index that is out of range for a sequence (like a list or string).ValueError
: Raised when a function receives an argument of the correct type but an inappropriate value.KeyError
: Raised when trying to access a dictionary key that doesn’t exist.
Handling Exceptions with try-except
Blocks
The core mechanism for handling exceptions in Python is the try-except
block. This allows you to anticipate potential errors and execute alternative code if an exception occurs.
try:
= 10 / 0 # Potential ZeroDivisionError
result except ZeroDivisionError:
print("Error: Division by zero!")
This code attempts to divide 10 by 0. Since this will raise a ZeroDivisionError
, the except
block catches it and prints an error message instead of crashing the program.
Handling Multiple Exceptions
A single try
block can have multiple except
blocks to handle different exception types:
try:
file = open("nonexistent_file.txt", "r")
= file.read()
data except FileNotFoundError:
print("Error: File not found!")
except Exception as e: # Catching any other exception
print(f"An unexpected error occurred: {e}")
finally:
file.close() # Always execute regardless of exceptions.
This example demonstrates handling both FileNotFoundError
and any other potential exception using a generic Exception
handler. The finally
block ensures that the file is closed, regardless of whether an exception occurred or not.
Raising Exceptions
You can also explicitly raise exceptions using the raise
keyword. This is useful for signaling errors in your own functions or methods:
def check_age(age):
if age < 0:
raise ValueError("Age cannot be negative")
elif age > 120:
raise ValueError("Age is unrealistically high")
return True
try:
-5)
check_age(except ValueError as e:
print(e)
This check_age
function raises a ValueError
if the input age is invalid.
Custom Exceptions
For more complex error handling, you can define your own custom exception classes by inheriting from the built-in Exception
class:
class InvalidInputError(Exception):
pass
def process_input(data):
if not data:
raise InvalidInputError("Input cannot be empty")
# ...rest of the processing...
This creates a custom exception InvalidInputError
that can be used to signal specific error conditions within your application.
Using else
and finally
Clauses
else
: An optionalelse
clause can be added after theexcept
block(s). The code within theelse
block executes only if no exceptions were raised in thetry
block.finally
: Thefinally
clause, also optional, contains code that always executes, regardless of whether an exception occurred or not. This is frequently used for cleanup tasks, such as closing files or releasing resources.
try:
# Some code that might raise an exception
= 10 / 2
x except ZeroDivisionError:
print("Error: Cannot divide by zero")
else:
print(f"Result: {x}")
finally:
print("This always executes.")