Exception Handling#
When an error occurs at runtime, Python raises an exception. If the exception isn’t handled, the program terminates and displays an error message:
1
2
3
4
5
6
7
8
9
| print(10 / 0)
# ZeroDivisionError: division by zero
int("hello")
# ValueError: invalid literal for int() with base 10: 'hello'
numbers = [1, 2, 3]
print(numbers[10])
# IndexError: list index out of range
|
try / except basic usage#
Wrap potentially failing code in try, and handle exceptions in except:
1
2
3
4
5
6
| try:
result = 10 / 0
except ZeroDivisionError:
print("Error: cannot divide by zero")
print("Program continues executing")
|
Catching multiple exception types#
1
2
3
4
5
6
7
8
9
10
11
| def safe_divide(a, b):
try:
return a / b
except ZeroDivisionError:
print("Divisor cannot be zero")
except TypeError:
print("Arguments must be numbers")
return None
safe_divide(10, 0) # Divisor cannot be zero
safe_divide(10, "two") # Arguments must be numbers
|
Single except with a tuple of exception types#
1
2
3
4
| try:
value = int(input("Enter a number: "))
except (ValueError, TypeError):
print("Invalid input format")
|
Catching all exceptions#
1
2
3
4
5
6
| try:
# Some uncertain operation
risky_operation()
except Exception as e:
print(f"An error occurred: {e}")
print(f"Exception type: {type(e).__name__}")
|
else and finally#
1
2
3
4
5
6
7
8
9
10
| try:
result = 10 / 2
except ZeroDivisionError:
print("Division by zero")
else:
# Runs when no exception occurred in the try block
print(f"Calculation successful: {result}")
finally:
# Always runs, whether or not an exception occurred
print("Calculation complete")
|
Typical use of finally: ensure resources are released (e.g., closing files or database connections):
1
2
3
4
5
6
7
8
9
| file = None
try:
file = open("data.txt", "r")
content = file.read()
except FileNotFoundError:
print("File not found")
finally:
if file:
file.close() # Always close, no matter what
|
Common exception types#
| Exception | When it occurs |
|---|
ValueError | Value has wrong format (e.g., int("abc")) |
TypeError | Wrong type (e.g., "2" + 2) |
ZeroDivisionError | Division by zero |
IndexError | List index out of range |
KeyError | Key not found in dictionary |
AttributeError | Object has no such attribute or method |
FileNotFoundError | Specified file not found |
NameError | Using an undefined variable |
ImportError | Unable to import a module |
StopIteration | Iterator has no more elements |
Getting exception info#
1
2
3
4
5
| try:
result = int("abc")
except ValueError as e:
print(f"Exception message: {e}")
print(f"Exception type: {type(e).__name__}")
|
Raising exceptions manually with raise#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| def set_age(age):
if not isinstance(age, int):
raise TypeError("Age must be an integer")
if age < 0 or age > 150:
raise ValueError(f"Age {age} is out of reasonable range (0-150)")
print(f"Age set to {age}")
try:
set_age(-5)
except ValueError as e:
print(f"Error: {e}")
try:
set_age("twenty")
except TypeError as e:
print(f"Error: {e}")
|
Custom exception classes#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
| class InsufficientFundsError(Exception):
"""Raised when account balance is insufficient"""
def __init__(self, balance, amount):
self.balance = balance
self.amount = amount
super().__init__(f"Insufficient funds: have {balance}, need {amount}")
class BankAccount:
def __init__(self, balance):
self.balance = balance
def withdraw(self, amount):
if amount > self.balance:
raise InsufficientFundsError(self.balance, amount)
self.balance -= amount
print(f"Withdrew {amount}, remaining balance: {self.balance}")
account = BankAccount(1000)
try:
account.withdraw(500) # Withdrew 500, remaining balance: 500
account.withdraw(800) # Exceeds balance
except InsufficientFundsError as e:
print(f"Transaction failed: {e}")
|
Practical examples#
Safe type conversion#
1
2
3
4
5
6
7
8
9
10
11
| def safe_int(value, default=0):
"""Try to convert a value to int; return default on failure"""
try:
return int(value)
except (ValueError, TypeError):
return default
print(safe_int("42")) # 42
print(safe_int("abc")) # 0
print(safe_int(None)) # 0
print(safe_int("3.7")) # 0 (int() does not accept decimal strings)
|
Retry mechanism#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| import random
def fetch_data():
"""Simulate an operation that may fail"""
if random.random() < 0.7: # 70% chance of failure
raise ConnectionError("Connection failed")
return "Data retrieved successfully"
max_retries = 3
for attempt in range(1, max_retries + 1):
try:
result = fetch_data()
print(result)
break
except ConnectionError as e:
print(f"Attempt {attempt} failed: {e}")
if attempt == max_retries:
print("Maximum retries reached, giving up")
|
1
2
3
4
5
6
7
8
9
10
11
12
13
| def get_positive_number(prompt):
"""Keep prompting the user until a valid positive integer is entered"""
while True:
try:
value = int(input(prompt))
if value <= 0:
raise ValueError("Must be a positive integer")
return value
except ValueError as e:
print(f"Input error: {e}. Please try again.")
# n = get_positive_number("Enter a positive integer: ")
# print(f"You entered: {n}")
|