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
148: Debugging with breakpoint()
We've all been there. You've got a bug that only triggers under a specific set of conditions, and your first instinct is to start peppering your code with print() statements. You print the input, you print the intermediate calculation, and you print "I got here" just to make sure the loop is actually running. For a while, it works. But then you realize you're spending more time adding and removing print statements than actually fixing the bug.
The Print-Statement Rabbit Hole
Let's look at a realistic scenario. Imagine you're writing a function that calculates a final price for an e-commerce cart, applying different discount tiers based on user membership and product categories. It looks something like this:
def calculate_total(cart, membership_level):
total = 0
for item in cart:
price = item['price']
if item['category'] == 'electronics' and membership_level == 'gold':
price *= 0.9
elif item['category'] == 'clothing':
price *= 0.8
total += price
return total
# This works for most, but fails for a specific "platinum" edge case
# where the logic is missing or behaving weirdly.
my_cart = [{'price': 100, 'category': 'electronics'}, {'price': 50, 'category': 'clothing'}]
print(calculate_total(my_cart, 'gold'))
When this fails, the "naive" approach is to add print(f"Debug: item is {item}, price is {price}") inside the loop. The problem is that printing is static. If you realize you actually needed to know the membership_level at the moment the electronics discount was applied, you have to stop the program, add another print statement, and run the whole thing from the beginning. If your data comes from a slow API or a heavy database query, this cycle becomes agonizingly slow. You're essentially guessing what you need to know before you actually know it.
Pausing Time with breakpoint()
This is where breakpoint() comes in. Instead of asking Python to tell you what happened after the code has already run, breakpoint() tells Python to stop exactly where you are and hand you the steering wheel. It's like hitting a pause button on a movie so you can walk onto the set and inspect the props.
If we drop breakpoint() into that loop, the experience changes completely:
for item in cart:
breakpoint() # The program freezes here
price = item['price']
# ... rest of the logic
When Python hits that line, it drops you into the PDB (Python Debugger) interactive shell. You aren't just looking at a log; you are inside the running process. You can type price to see its current value, or item['category'] to verify the data. More importantly, you can test hypotheses in real-time. You can try typing price * 0.9 to see if that's the value you expected, or even change the value of a variable on the fly to see how the rest of the function reacts. I've spent hours of my life saving days of work just by changing a variable mid-execution to see if a specific fix would work before actually writing the code.
When to Stop Printing and Start Probing
Now, I'm not saying print() is evil. For a quick check of a single variable in a three-line script, it's perfectly fine. But there's a mental shift that happens when you move to breakpoint(). You stop treating your code as a "black box" that you feed inputs into and hope for the right output, and you start treating it as a living environment.
The trade-off is mainly about the "flow." Printing is non-intrusive; the program runs to completion. breakpoint() is intrusive; it halts everything. If you're debugging a multi-threaded application or something that relies on a strict timeout (like a network socket), a breakpoint can actually cause the very timeout you're trying to debug. But for the vast majority of logic errors, the ability to probe the state of your application interactively is a superpower. Once you get used to the PDB commands—like n for next line, s to step into a function, and c to continue execution—you'll find that you rarely go back to the "print and pray" method.
📋 Practical Task
Fixing the Inventory Miscount
You have been handed a piece of code that manages a warehouse inventory. The goal is to update the stock levels based on a list of shipments, but there is a bug: some items are being subtracted when they should be added, and some are disappearing entirely.
Your Task: Use breakpoint() to diagnose the issue. Instead of adding print statements, place a breakpoint inside the process_shipments loop. Once the debugger opens, inspect the action and quantity variables to find why the inventory dictionary is ending up with incorrect values. Fix the logic error in the code and ensure the final inventory count is correct.
def process_shipments(inventory, shipments):
for shipment in shipments:
item = shipment['item']
quantity = shipment['quantity']
action = shipment['action']
# BUG: The logic here is slightly flawed.
# It should add for 'receive' and subtract for 'ship'.
if action == 'receive':
inventory[item] -= quantity
elif action == 'ship':
inventory[item] += quantity
return inventory
current_inventory = {'widgets': 10, 'gadgets': 5}
shipment_logs = [
{'item': 'widgets', 'quantity': 5, 'action': 'receive'},
{'item': 'gadgets', 'quantity': 2, 'action': 'ship'},
{'item': 'widgets', 'quantity': 3, 'action': 'ship'},
]
# The expected result should be {'widgets': 12, 'gadgets': 3}
result = process_shipments(current_inventory, shipment_logs)
print(f"Final Inventory: {result}")
There are no comments for now.