Python
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax and Data Types
-
Section 3: Collections
-
39: Set Operations: Union, Intersection, Difference
-
Section 4: Control Flow
-
Section 5: Functions
-
Section 6: Turtle Graphics and Early Practice Projects
-
Section 7: Working with Files and I/O
-
Section 8: Regular Expressions
-
Section 9: Object-Oriented Python
-
Section 10: Error Handling
-
Section 11: Modules and Packages
-
Section 12: Iterators, Generators, and Functional Tools
-
Section 13: Decorators and Metaprogramming
-
Section 14: Concurrency and Parallelism
-
Section 15: Working with Dates, Times, and Numbers
-
Section 16: Standard Library Deep Dive I: Data Structures
-
Section 17: Standard Library Deep Dive II: System and Introspection
-
Section 18: Standard Library Deep Dive III: Security and Encoding
-
Section 19: Standard Library Deep Dive IV: Text and Data Utilities
-
Section 20: Networking and Web Basics
-
Section 21: Working with Databases
-
Section 22: Testing and Quality
-
Section 23: Advanced Typing
-
Section 24: Context Managers and Resource Handling
-
Section 25: Text, Unicode, and Binary Data
-
Section 26: More Functional and Iteration Tools
-
Section 27: Data Validation and Configuration
-
Section 28: Working with Images and Media
-
Section 29: Property-Based and Documentation Testing
-
Section 30: Packaging and Deployment
-
Section 31: Performance and Internals
-
Section 32: Design Patterns in Python
-
Section 33: GUI Programming
-
Section 34: Security Basics
-
Section 35: Data Structures and Algorithms
-
Section 36: Practical Projects
-
Section 37: Capstone Projects
-
Section 38: Interview and Algorithm Practice
-
Section 39: Writing Idiomatic Python
140: Catching Multiple Exception Types
In the previous lessons, we looked at how to wrap risky code in a try block and handle a single exception. That works great when there's only one thing that can go wrong, but real-world code is messier. Usually, a single block of logic can fail for several different reasons, and you have to decide if those reasons deserve the same response or different ones.
Building a simple payment processor
Let's build a small function that processes a payment amount. I want this function to take a value, convert it to a float, and then ensure it's positive. It sounds simple, but users are unpredictable. They might pass in a string that isn't a number, or they might pass in None by accident.
def process_payment(amount):
try:
# Convert to float in case it's a string
numeric_amount = float(amount)
if numeric_amount <= 0:
raise ValueError("Amount must be positive.")
print(f"Processing payment of ${numeric_amount:.2f}")
except ValueError:
print("Invalid amount entered. Please use a number.")
This handles the ValueError, but if someone passes None into the function, float(None) raises a TypeError. Right now, my program would just crash. I could add another except block, but here is where I usually start thinking about efficiency.
Grouping similar failures
In this specific case, whether it's a ValueError (like the string "abc") or a TypeError (like the value None), the end result is the same: the input is garbage. I don't need two different error messages for the user; I just need them to provide a valid number.
Instead of writing two separate blocks with the exact same print statement, I can catch multiple exceptions in one go by passing them as a tuple to the except clause.
def process_payment(amount):
try:
numeric_amount = float(amount)
if numeric_amount <= 0:
raise ValueError("Amount must be positive.")
print(f"Processing payment of ${numeric_amount:.2f}")
except (ValueError, TypeError):
print("Invalid input. Please provide a valid positive number.")
Notice the parentheses around ValueError, TypeError. This is crucial. If you forget the parentheses, Python thinks you're trying to assign the exception instance to a variable named TypeError, which will lead to a very confusing bug.
Correcting a logic mistake
I just realized I made a mistake in my design. By grouping ValueError and TypeError together, I've accidentally lumped my "Amount must be positive" check in with the "This isn't a number" check. Both raise a ValueError, but they are very different problems. One is a data type issue; the other is a business logic issue.
If I want to tell the user why it failed—distinguishing between "that's not a number" and "you can't pay a negative amount"—I need to be more surgical. I'll keep the TypeError grouped with the conversion error, but I'll pull the business logic check into its own handler.
def process_payment(amount):
try:
# This part can throw TypeError or ValueError (if it's a bad string)
numeric_amount = float(amount)
if numeric_amount <= 0:
# I'll use a custom message here
raise ValueError("Negative amount")
print(f"Processing payment of ${numeric_amount:.2f}")
except (TypeError, ValueError) as e:
# Check if it was my specific "Negative amount" error
if str(e) == "Negative amount":
print("Error: You cannot process a payment of zero or less.")
else:
print("Error: The input provided is not a valid number.")
Actually, an even cleaner way to do this is just to order my except blocks from most specific to most general. But the tuple approach is your best friend whenever you have a handful of different exceptions that truly require the exact same recovery steps.
📋 Practical Task
Exercise: The Robust Configuration Loader
You are writing a function load_config(config_dict, key) that retrieves a setting from a dictionary. The function should try to convert the value to an integer.
Write the function so that it handles the following scenarios using a tuple of exceptions in a single except block:
- The key is missing from the dictionary (
KeyError). - The value associated with the key cannot be converted to an integer (
ValueError). - The value associated with the key is a type that
int()cannot handle, such as a list (TypeError).
If any of these three errors occur, the function should print "Configuration error: Invalid or missing setting." and return a default value of 0. If the value is successfully converted, return the integer.
There are no comments for now.