Python Basics - 07. Errors and Exceptions¶
Even the best code encounters unexpected issues: network timeouts, missing files, or invalid user inputs. Rather than having the whole script crash abruptly, Python allows you to handle these “Exceptions” gracefully.
Download Notebook¶
1. Common Exceptions Overview¶
Here are a few standard exceptions you might encounter:
SyntaxError: Incorrect Python grammar (must be fixed before running, cannot be caught dynamically in the same block).TypeError: Operation applied to an inappropriate type (e.g., adding string to int).NameError: Calling a variable that hasn’t been defined.ZeroDivisionError: Trying to divide by zero.IndexError: Accessing an out-of-bounds index in a list.
2. Using Try-Except Blocks¶
Wrap potentially dangerous code inside a try block, and handle the specific error inside an except block.
try:
# This will cause a zero division error
result = 10 / 0
except ZeroDivisionError:
print("Error Successfully Caught: You cannot divide a number by zero!")
except Exception as e:
# A generic fallback for other unexpected errors
print(f"Caught unexpected error: {e}")
Error Successfully Caught: You cannot divide a number by zero!
3. Utilizing Else and Finally¶
else: This block is executed only if no exception occurs inside the try block.finally: This block is executed no matter what, regardless of whether an exception took place. It is typically used for cleaning up resources (e.g. closing network connections).
def safe_divide(x, y):
print(f"\nAttempting to divide {x} by {y}...")
try:
result = x / y
except ZeroDivisionError:
print("[Exception] Division by zero!")
return None
else:
# Runs if 'try' succeeds
print(f"[Else] Division successful, result is: {result}")
return result
finally:
# Runs in all scenarios
print("[Finally] Execution of try-except block is complete.")
safe_divide(10, 2)
safe_divide(10, 0)
Attempting to divide 10 by 2...
[Else] Division successful, result is: 5.0
[Finally] Execution of try-except block is complete.
Attempting to divide 10 by 0...
[Exception] Division by zero!
[Finally] Execution of try-except block is complete.
4. Raising Exceptions Manually¶
In your own logic, you can force an error to occur if certain conditions are violated using the raise keyword.
def register_user(age):
if age < 0:
# We intentionally stop execution and throw our own error
raise ValueError("A user's age cannot be negative!")
print(f"User registered successfully with age: {age}")
try:
register_user(-5)
except ValueError as custom_error:
print(f"Validation Failed: {custom_error}")
Validation Failed: A user's age cannot be negative!
5. Custom Exceptions and Assertions¶
Custom exceptions make error handling expressive in larger systems.
assert is useful during development to check assumptions, but it should not replace full runtime validation for user-facing programs.
class InvalidScoreError(Exception):
"""Raised when score is outside valid range [0, 100]."""
def normalize_score(score):
# Validate user input before business logic.
if not (0 <= score <= 100):
# Raise a domain-specific exception for clearer handling.
raise InvalidScoreError(f"Invalid score: {score}")
return score / 100
for value in [95, 120, 75]:
try:
print(f"Normalized: {normalize_score(value):.2f}")
except InvalidScoreError as e:
# Catch specific exception type first.
print(f"Handled custom exception: {e}")
# Assertion example
# Useful for development-time sanity checks.
x = 10
assert x > 0, "x must be positive"
print("Assertion passed")
Normalized: 0.95
Handled custom exception: Invalid score: 120
Normalized: 0.75
Assertion passed