1. Introduction to Exceptions
An exception is an unexpected event that occurs during program execution. It disrupts the normal flow of the program's instructions.
Common scenarios causing exceptions:
- Dividing a number by zero.
- Accessing an index outside list bounds.
- Opening a file that does not exist.
- Converting an invalid string to an integer.
- Importing a module that does not exist.
Exception vs Error: An error indicates a serious problem that should not be caught (syntax errors), while an exception can be caught and handled.
2. Common Built-in Exceptions
| Exception | Cause | Example |
|---|---|---|
ZeroDivisionError | Division or modulo by zero | 10 / 0 |
ValueError | Invalid value for a function | int("abc") |
TypeError | Invalid operation between types | "2" + 3 |
IndexError | List index out of range | lst[10] |
KeyError | Dictionary key not found | d["xyz"] |
FileNotFoundError | File does not exist | open("nofile.txt") |
ImportError | Module import fails | import nonexistent |
AttributeError | Attribute or method not found | "abc".nonexistent() |
StopIteration | Iterator has no more items | next(empty_iter) |
3. The try-except Block
The try block contains code that may raise an exception. The except block handles the exception if it occurs.
try:
num = int(input("Enter a number: "))
result = 10 / num
print("Result:", result)
except ZeroDivisionError:
print("Cannot divide by zero.")
except ValueError:
print("Invalid input. Please enter a valid number.")
Syntax:
try:
# Code that may raise exception
except ExceptionType:
# Handle the exception
4. Handling Multiple Exceptions
Multiple exceptions can be handled in two ways:
Multiple except blocks:
try:
x = int(input("Enter first number: "))
y = int(input("Enter second number: "))
print(x / y)
except ZeroDivisionError:
print("Division by zero not allowed.")
except ValueError:
print("Invalid input. Enter numeric values.")
Single except with tuple:
try:
x = int(input("Enter a number: "))
result = 100 / x
print(result)
except (ZeroDivisionError, ValueError) as e:
print("Error occurred:", e)
5. The else Clause
The else block executes only when no exception occurs in the try block.
try:
num = int(input("Enter a number: "))
result = 100 / num
except ZeroDivisionError:
print("Cannot divide by zero.")
except ValueError:
print("Invalid input.")
else:
print("Division successful. Result:", result)
6. The finally Clause
The finally block always executes regardless of whether an exception occurs or not. It is commonly used for cleanup operations like closing files or releasing resources.
try:
f = open("data.txt", "r")
content = f.read()
except FileNotFoundError:
print("File not found.")
finally:
f.close()
print("File closed.")
7. Raising Exceptions with raise
The raise statement is used to explicitly raise an exception. It can be used both inside and outside try blocks.
def check_age(age):
if age < 0:
raise ValueError("Age cannot be negative.")
elif age > 150:
raise ValueError("Age seems unrealistic.")
else:
print("Valid age:", age)
try:
check_age(-5)
except ValueError as e:
print("Error:", e)
Re-raising an exception:
try:
x = int("abc")
except ValueError:
print("Invalid input caught.")
raise # Re-raise the same exception
8. The assert Statement
The assert statement is used for debugging. It raises an AssertionError if a condition is False.
def divide(a, b):
assert b != 0, "Denominator cannot be zero"
return a / b
print(divide(10, 2)) # Output: 5.0
print(divide(10, 0)) # Raises AssertionError
Assertions can be disabled with the -O (optimize) flag when running Python.
9. Creating Custom Exceptions
You can create custom exceptions by defining a new class that inherits from the built-in Exception class.
class NegativeNumberError(Exception):
"""Raised when a negative number is provided."""
pass
class InsufficientFundsError(Exception):
def __init__(self, balance, amount):
super().__init__(f"Insufficient funds. Balance: {balance}, Required: {amount}")
self.balance = balance
self.amount = amount
def withdraw(balance, amount):
if amount > balance:
raise InsufficientFundsError(balance, amount)
return balance - amount
10. Getting Exception Information
Use the as keyword to capture exception details.
try:
x = 10 / 0
except ZeroDivisionError as e:
print("Exception type:", type(e).__name__)
print("Exception args:", e.args)
print("Exception message:", str(e))
The sys.exc_info() function provides detailed exception information including traceback.
11. Nested try-except Blocks
try:
try:
num = int(input("Enter number: "))
result = 10 / num
print("Result:", result)
except ZeroDivisionError:
print("Inner: Division by zero.")
except ValueError:
print("Outer: Invalid input.")
If the inner block does not handle an exception, it propagates to the outer block.
12. Best Practices
- Catch specific exceptions instead of using a bare
except:. - Use
finallyfor cleanup operations (closing files, releasing resources). - Avoid catching exceptions and then silently ignoring them.
- Use
elseblock for code that should run only when no exception occurs. - Raise meaningful exceptions with descriptive messages.
- Do not use exceptions for normal control flow.
- Log exception details for debugging purposes.
- Keep try blocks small and focused.
13. Revision Questions and Answers
Very Short Answer Questions
1. What is an exception?
An exception is an unexpected event that occurs during program execution and disrupts normal flow.
2. Which keywords are used for exception handling in Python?try, except, else, finally, and raise.
3. What is the purpose of the finally block?
The finally block always executes, regardless of whether an exception occurred or not, and is used for cleanup operations.
4. Which statement is used to manually raise an exception?
The raise statement.
5. What does the else block do in a try-except statement?
The else block executes only when no exception occurs in the try block.
Short Answer Questions
1. Differentiate between except and finally.
The except block executes only when a specific exception occurs in the try block. The finally block always executes regardless of whether an exception occurred, and is primarily used for cleanup tasks like closing files or releasing resources.
2. How can you handle multiple exceptions in a single try block?
Multiple exceptions can be handled using multiple except blocks (one for each exception type) or a single except block with a tuple of exception types: except (ValueError, TypeError) as e:.
Long Answer Questions
1. Write a program that handles division by zero, invalid input, and includes else and finally blocks.
try:
numerator = float(input("Enter numerator: "))
denominator = float(input("Enter denominator: "))
result = numerator / denominator
except ZeroDivisionError:
print("Error: Cannot divide by zero.")
except ValueError:
print("Error: Please enter numeric values only.")
else:
print(f"Result: {result:.2f}")
finally:
print("Execution completed.")
2. Explain the difference between syntax errors and exceptions with examples.
Syntax errors occur when Python cannot parse the code due to incorrect grammar, such as missing a colon. They prevent the program from running. Exceptions occur during execution of syntactically correct code, such as dividing by zero. Syntax errors must be fixed before running; exceptions can be caught and handled using try-except blocks.