3.5 Exception Handling (try, except, finally)
In Python, errors and exceptions can occur during the execution of a program, and if they are not handled properly, they can cause the program to crash. Exception handling allows you to manage errors gracefully and prevent your program from failing unexpectedly. Python provides a built-in mechanism for handling exceptions using the try
, except
, else
, and finally
blocks. In this section, we'll explore how to use these blocks to handle exceptions effectively.
3.5.1 What is an Exception?
An exception is an error that occurs during the execution of a program. When an error is encountered, Python stops the program and raises an exception. If the exception is not handled, the program will terminate and display a traceback (error message).
Common exceptions in Python include:
ZeroDivisionError
: Raised when dividing by zero.TypeError
: Raised when an operation or function is applied to an object of an inappropriate type.ValueError
: Raised when a function receives an argument of the right type but inappropriate value.FileNotFoundError
: Raised when trying to open a file that doesn’t exist.IndexError
: Raised when trying to access an invalid index in a sequence.
Example of an Unhandled Exception:
x = 10 / 0 # Raises ZeroDivisionError
Output:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ZeroDivisionError: division by zero
In this case, the program crashes due to an unhandled ZeroDivisionError
.
3.5.2 The try
and except
Blocks
The try
and except
blocks are used to handle exceptions. Code that might raise an exception is placed inside the try
block, and the code to handle the exception is placed inside the except
block.
Syntax:
try:
# Code that may raise an exception
except ExceptionType:
# Code to handle the exception
Example 1: Handling a ZeroDivisionError:
try:
result = 10 / 0
except ZeroDivisionError:
print("Error: Cannot divide by zero!")
Output:
Error: Cannot divide by zero!
In this example, the ZeroDivisionError
is caught by the except
block, and the program does not crash. Instead, it prints an error message and continues.
Example 2: Handling Multiple Exceptions:
You can handle multiple exceptions by specifying different except
blocks for different error types.
try:
x = int(input("Enter a number: "))
result = 10 / x
except ZeroDivisionError:
print("Error: Division by zero is not allowed.")
except ValueError:
print("Error: Invalid input. Please enter a valid number.")
Output (if user enters a string):
Error: Invalid input. Please enter a valid number.
In this example, if the user enters 0
, a ZeroDivisionError
is raised, and if the user enters a non-numeric value, a ValueError
is raised. Each error is handled by the appropriate except
block.
Example 3: Catching Any Exception
You can catch any exception using a generic except
block without specifying an exception type.
try:
result = 10 / 0
except:
print("An error occurred.")
Output:
An error occurred.
While this approach is simple, it’s generally better to catch specific exceptions so that you can handle different errors appropriately.
3.5.3 The else
Block
The else
block is used to define code that should run only if no exceptions were raised in the try
block. If an exception occurs, the else
block will not be executed.
Syntax:
try:
# Code that may raise an exception
except ExceptionType:
# Code to handle the exception
else:
# Code to execute if no exception occurs
Example:
try:
result = 10 / 2
except ZeroDivisionError:
print("Error: Division by zero.")
else:
print(f"Result is: {result}")
Output:
Result is: 5.0
In this case, since no exception occurs, the code inside the else
block is executed, and the result is printed.
3.5.4 The finally
Block
The finally
block is used to define code that will run no matter what happens—whether an exception was raised or not. This block is often used for cleanup operations, such as closing files or releasing resources.
Syntax:
try:
# Code that may raise an exception
except ExceptionType:
# Code to handle the exception
finally:
# Code that will run no matter what
Example:
try:
result = 10 / 2
except ZeroDivisionError:
print("Error: Division by zero.")
finally:
print("This block runs no matter what.")
Output:
This block runs no matter what.
Result is: 5.0
In this example, the finally
block runs whether or not an exception occurs. This is useful when you need to ensure certain actions are always performed, like closing a file.
3.5.5 Raising Exceptions Manually
You can manually raise exceptions in your code using the raise
statement. This can be useful for signaling errors when specific conditions are met in your program.
Syntax:
raise ExceptionType("Error message")
Example:
def divide(a, b):
if b == 0:
raise ValueError("Cannot divide by zero!")
return a / b
try:
result = divide(10, 0)
except ValueError as e:
print(e)
Output:
Cannot divide by zero!
In this example, the ValueError
is manually raised inside the divide()
function when an attempt is made to divide by zero.
3.5.6 Exception Object (as
)
When an exception is caught, Python creates an exception object that contains information about the error. You can capture this object using the as
keyword and print or log the error details.
Syntax:
try:
# Code that may raise an exception
except ExceptionType as e:
print(e)
Example:
try:
result = 10 / 0
except ZeroDivisionError as e:
print(f"An error occurred: {e}")
Output:
An error occurred: division by zero
In this case, the exception message ("division by zero") is stored in the variable e
, which can be used to display detailed error information.
3.5.7 Catching Multiple Exceptions with a Single except
Block
You can catch multiple types of exceptions in a single except
block by using a tuple of exception types.
Syntax:
try:
# Code that may raise an exception
except (ExceptionType1, ExceptionType2):
# Code to handle either exception
Example:
try:
x = int(input("Enter a number: "))
result = 10 / x
except (ZeroDivisionError, ValueError):
print("Error: Invalid input or division by zero.")
Output (if user enters 0 or non-numeric input):
Error: Invalid input or division by zero.
In this example, both ZeroDivisionError
and ValueError
are handled by the same except
block.
3.5.8 Best Practices for Exception Handling
- Use
finally
for Cleanup: Always use thefinally
block for resource cleanup, like closing files or releasing resources. - Don’t Suppress Exceptions: Avoid using bare
except
blocks that catch all exceptions without handling them properly. This can hide bugs and make debugging difficult. - Raise Exceptions When Necessary: Raise exceptions when you need to signal that an operation has failed. Use custom error messages to provide context for the error.
Be Specific: Catch specific exceptions rather than using a generic except
block. This makes debugging easier and helps avoid catching unintended exceptions.Example:
try:
result = 10 / 0
except ZeroDivisionError:
print("Division by zero error.")
3.5.9 Summary
try
block: Contains code that may raise an exception.except
block: Handles the exception if one is raised.else
block: Executes if no exception occurs.finally
block: Runs whether an exception occurs or not, often used for cleanup tasks.raise
statement: Used to manually raise exceptions when needed.- Exception object (
as
): Captures the
error message or details of the exception for use in error handling.
Effective exception handling allows your Python programs to manage errors gracefully and continue running, even when things go wrong.