Python: Exceptions
Exceptions in Python
1 Overview
📘 Exceptions in Python
In Python, exceptions
are events that disrupt the normal flow of a program when an error occurs during execution. They differ from syntax errors, which are detected before the code runs. Exceptions signal that something unexpected happened while the program was running—such as dividing by zero, accessing a missing file, or using an undefined variable—and allow developers to handle these situations gracefully rather than letting the program crash.
Python provides a structured mechanism to deal with such errors through try
and except
blocks. The try
block encloses potentially error-prone code, while the except
block contains instructions for handling specific exceptions if they occur. You can also use else
(which runs when no errors occur) and finally
(which always executes) to manage code execution more precisely
2 Why Handle Exceptions?
- Prevent application crashes
- Provide user-friendly messages
- Safely recover from errors
- Ensure clean resource management
3 Common Exceptions
ValueError: Invalid value.
ValueError: invalid literal for int() with base 10: ‘hola’
TypeError: Incorrect data type.
TypeError: unsupported operand type(s) for +: ‘int’ and ‘str’
IndexError: List/Tuple index out of range.
IndexError: list index out of range
KeyError: Dictionary key not found.
KeyError: ‘c’
4 Try-Except Block
- Used to capture and handle exceptions.
- Different exceptions, different logic.
- Allows for graceful failure.
- Improves user experience: No unexpected crashes.
Fatal error, the whole program stops:
ZeroDivisionError: division by zero
Handle potential errors gracefully:
Output:
Can't divide by zero!
Program continues to run...
Without proper error handling, our programs will crash anyway.
Output:
IndexError: list index out of range
Handling Any Exception
Output:
An error occurred
Output:
An error occurred
Handle as many code as we want in a try-except block
def complicated_function():
# Assume a function that is very complicated
# # and can raise many different exceptions a = [1, 2, 3] return a[4] / 0 + "hello"
a = [1, 2, 3]
return a[4] / 0 + "hello"
try:
print("Can handle a lot of code")
complicated_function()
print("This will not be printed")
except:
print("An error occurred")
Output:
Can handle a lot of code
An error occurred
5 Multiple Errors Exception
Handle multiple types of exceptions at once applying the same logic.
6 Multiple or Separate Exception Blocks
- Handle different exceptions separately.
- More precise error handling.
7 Using “as” in Except
Use as [variable name]
to access the original error message object.
Output:
Can't divide by zero!
Original error message: "division by zero"
Error type: <class 'ZeroDivisionError'>
8 Else Block
else
: Executes when thetry
block doesn’t raise exceptions.- Useful for code that should run only when no errors occurred.
Output:
Try block code
No exception raised!
9 Finally Block
finally
: Always runs, whether an exception was raised or not.- Essential for cleanup tasks, like closing files or releasing resources.
Output:
Try block code
Always runs!
With an error.
Output:
Handle ValueError
Always runs!
With an unhandled error.
Output:
Always runs!
Even if the exception is not caught.
Everything together.
Output:
Try block code
No exception raised!
Always runs!
ANother example.
Output:
Handle ZeroDivisionError
Always runs!
10 Raising Exceptions
- Allows developers to define specific error conditions.
- Can be used to notify other parts of the software about issues.
- Promotes building more predictable and safer programs.
Error:
Age cannot be negative!
or.
Error:
Are you sure the person is that old (125)?!
11 Custom Exceptions
- Define specific error types for your application.
- Inherit from
Exception
for user-defined exceptions. - Improves error categorization and handling.
class AgeNegativeError(Exception):
"""Raised when the age is negative."""
pass
def calculate_birth_year(age):
if age < 0:
# We can raise a custom error with a message
raise AgeNegativeError("Age should not be negative!")
try:
birth_year = calculate_birth_year(-5)
except AgeNegativeError as e:
print(f"Error: {e}")
birth_year = None
if birth_year is not None:
print(f"Birth year: {birth_year[0]} or {birth_year[1]}")
Output:
Error: Age should not be negative!
12 Assert Statement
- Debugging aid that tests a condition.
- Ensure that an expression is
True
. - If
assert
condition evaluates toFalse
, anAssertionError
is raised. - Syntax:
assert [expression], ["Optional error message"]
Not raises exception:
If failed, it raises exception:
Behind the scenes
assert
is mostly just syntactic shorthand for araise
.- It works like the following:
13 Common Use Cases
- Sanity checks: Ensure code behaves as expected.
- Preconditions: Validate input or environment before executing.
- Postconditions: Confirm the output or effect of a function.
- Testing: Used in testing frameworks for validating the correctness of a function.
14 Example 1: Precondition
15 Example 2: Test Assertion
Output:
Tests passed!
16 Dangers
assert
can be globally disabled with the-O
(optimize) command line switch.- Not suitable for production error handling.
- Use exceptions for errors that shouldn’t be ignored.
17 Exception Design Tips
Use
try-except
:For operations that fail often (e.g., socket calls, file opens, etc.)
For unpredictable user inputs (e.g., using a string when it should be an int.)
For unreliable third-party libraries (e.g., API calls.)
For long-running systems that must not crash (e.g., bank transactions (milions per day…).)
For simple scripts you may want failures to kill your program.
Example:
for transaction in transactions:
try:
process_transaction(transaction)
except DatabaseConnectionError:
time.sleep(60)
retry_transaction(transaction)
except SuspiciousTransactionError as e:
flag_transaction(transaction, reason=e)
except Exception as e:
log_error(e)
move_to_failed_queue(transaction)
finally:
update_transaction_status(transaction)
close_transaction(transaction)