-
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
138: Practice Exercise: Building a Simple Inventory System with OOP
By this point in the course, you know how to define a class and instantiate an object. But there is a big difference between knowing the syntax and knowing why you'd actually choose an object-oriented approach over a simpler one. I see a lot of developers stumble here; they either over-engineer everything into classes or they avoid them entirely, sticking to dictionaries and lists because "it's faster to write."
The "Dictionary Soup" Approach
Let's say we're building a simple inventory system for a small electronics shop. If you were tackling this naively, you'd probably start with a dictionary. It feels intuitive. You have a product name as a key, and another dictionary as the value to hold the quantity and price. It looks something like this:
inventory = {
"Mechanical Keyboard": {"qty": 15, "price": 120.00},
"Gaming Mouse": {"qty": 30, "price": 60.00},
"Ultrawide Monitor": {"qty": 5, "price": 450.00}
}
def update_stock(item_name, amount):
if item_name in inventory:
inventory[item_name]["qty"] += amount
else:
print("Item not found!")
At first glance, this is lean. But here is where it starts to break. Because you're relying on "stringly typed" keys like "qty" and "price", you're one typo away from a KeyError that crashes your program in production. If I accidentally type inventory[item_name]["quantity"] in one function but "qty" in another, Python won't warn me until the code actually runs and fails. Plus, if you want to add logic—say, a method to calculate the total value of a specific item's stock—you have to write a standalone function and pass the dictionary around everywhere. The data and the logic are living in two different neighborhoods.
Encapsulating Logic with Item and Inventory Classes
The better way is to treat an "Item" as a first-class citizen. By creating an Item class, we define a strict blueprint. We aren't just storing data; we're defining what an item is and what it can do.
class Item:
def __init__(self, name, qty, price):
self.name = name
self.qty = qty
self.price = price
def total_value(self):
return self.qty * self.price
class Inventory:
def __init__(self):
self.items = {}
def add_item(self, item):
self.items[item.name] = item
def adjust_stock(self, name, amount):
if name in self.items:
self.items[name].qty += amount
else:
raise ValueError(f"Item {name} not found in inventory.")
Now, look at the trade-off. Yes, I wrote more lines of code. I had to define two classes instead of one dictionary. But I've gained something massive: predictability. I no longer have to remember if I called the key "qty" or "quantity" because my IDE's autocomplete will tell me exactly what Item attributes exist.
More importantly, the total_value logic now lives inside the Item class. If the way we calculate value changes tomorrow (maybe we add a tax rate or a discount), I only have to change it in one place. The Inventory class doesn't need to know how an item calculates its value; it just knows that the item can do it. This is the core of OOP—delegating responsibility. The Inventory manages the collection, and the Item manages its own data.
When the Complexity Pays Off
You might wonder if this is overkill for a few items. Maybe. But imagine if this system grows. What if some items are "Digital" (no shipping cost) and some are "Physical" (require warehouse space)? With the dictionary approach, you'd be adding if "type" == "digital" checks all over your codebase. With OOP, you just create a DigitalItem subclass that inherits from Item and overrides the specific methods it needs. You've built a foundation that can grow without becoming a tangled mess of nested dictionaries.
📋 Practical Task
Exercise: The Warehouse Stock Auditor
You need to expand the inventory system to handle a "low stock" alert system. Your goal is to build a system that can track items and identify which ones need to be reordered.
Requirements:
- Create an
Itemclass withname,qty, andreorder_level(the minimum quantity allowed before an alert is triggered). - Create an
Inventoryclass that stores these items in a dictionary. - Implement a method in
Inventorycalledget_reorder_list()that returns a list of names of all items whoseqtyis less than or equal to theirreorder_level. - Implement a method
sell_item(name, amount)that reduces the quantity. If the sale would result in a negative quantity, it should raise aValueError.
Test your code with this scenario:
1. Add a "Laptop" with qty 10 and reorder_level 5.
2. Add a "Mouse" with qty 3 and reorder_level 5.
3. Sell 6 Laptops.
4. Call get_reorder_list() and verify that both "Laptop" and "Mouse" are now on the list.
There are no comments for now.