Python | 3 Deep Dive Part 4 Oop [top]
Once upon a time in the sprawling digital kingdom of Pythoria, there lived a visionary architect named Guido. In the early days, the citizens of Pythoria wrote instructions as long, winding scrolls of code—logical, but messy. As the kingdom grew, Guido realized they needed a better way to build.
He introduced Object-Oriented Programming (OOP), not just as a tool, but as a philosophy: "Everything is an object." Chapter 1: The Blueprint and the Building
In Pythoria, if you wanted to build a house, you didn't just start stacking bricks. You created a Class.
A Class is like a blueprint. It doesn’t exist physically; it’s just a set of instructions. When you use that blueprint to actually build a house, you create an Instance (or an Object).
class House: def __init__(self, color, style): self.color = color # Attribute self.style = style # Attribute Use code with caution. Copied to clipboard
The __init__ method is the "foundation stone" ceremony. It’s a dunder (double underscore) method that sets up the object’s initial state the moment it's born. Chapter 2: The Secret Vaults (Encapsulation)
The wealthy merchants of Pythoria loved their privacy. They didn't want just anyone touching their gold. They used Encapsulation.
By putting a double underscore before a variable name (like __gold), they "mangled" the name, making it hard for outsiders to access directly. Instead, they provided Getters and Setters—controlled doorways to interact with their private data. Chapter 3: The Family Tree (Inheritance) python 3 deep dive part 4 oop
As the kingdom evolved, they realized they didn't need a brand-new blueprint for every single type of building. A Mansion is just a House with extra flair.
Through Inheritance, the Mansion class could "inherit" all the traits of the House class, then add its own unique features, like a ballroom or a moat.
class Mansion(House): def __init__(self, color, style, has_moat=True): super().__init__(color, style) # Calling the parent blueprint self.has_moat = has_moat Use code with caution. Copied to clipboard Chapter 4: The Shapeshifters (Polymorphism)
The most magical part of Pythoria was Polymorphism. It meant "many forms."
Imagine a royal decree that said: "Every building must describe_itself()." The House would say: "I am a cozy cottage." The Mansion would say: "I am a sprawling estate."
Even though the command was the same, each object reacted in its own unique way based on its identity.
Chapter 5: The Spirits of the Code (Descriptors and Properties) Once upon a time in the sprawling digital
In the Deep Dive (Part 4), we discover the "Old Magic": Descriptors. These are objects that manage the attributes of other objects. When you use the @property decorator, you are actually using a simplified version of a descriptor.
It allows you to turn a simple attribute into a computed value, or add validation logic behind the scenes without the user ever knowing. It's like a smart thermostat that adjusts the temperature automatically when you try to set it too high. The Moral of the Story
Object-Oriented Programming in Python 3 isn't just about making code "organized." It's about creating reusable, scalable, and protected systems. By mastering classes, inheritance, and the magic of dunder methods, you move from being a mere scripter to a Master Architect of Pythoria.
This report assumes you have a basic understanding of Python classes but want to explore the deeper mechanics, advanced patterns, and internals of Python’s OOP model.
Output:
9. Common Pitfalls and Best Practices
| Pitfall | Solution |
|---------|----------|
| Mutable default arguments | def __init__(self, items=None): if items is None: items = [] |
| Forgetting to call super().__init__() in multiple inheritance | Always use super() in every class that participates in cooperative inheritance |
| Using __slots__ and then wondering why dynamic attributes fail | Understand __slots__ disables __dict__ |
| Deep inheritance > 3 levels | Prefer composition or interfaces (ABCs/protocols) |
| Overusing metaclasses | 99% of needs solved by class decorators or __init_subclass__ (Python 3.6+) |
6. Protocols – Informal Interfaces
Protocols are not enforced by the language but expected by the runtime.
Conclusion
In this write-up, we explored the fundamental principles of Object-Oriented Programming (OOP) in Python 3, including classes, objects, inheritance, polymorphism, and encapsulation. We also provided examples to illustrate each concept. By applying these concepts, you can write more organized, maintainable, and efficient code. Output: 9
5.1 When to Use a Metaclass
Use metaclasses when you need to:
- Automatically register subclasses.
- Modify class attributes at creation time.
- Enforce coding standards across an entire API.
3.1 Attribute lookup chain
Instance → class → parent classes → __getattr__ → __getattribute__ → AttributeError
Chapter 4 — Composition over Inheritance
When the library grew, Lina added a Catalog and LoanManager. Instead of bloating Media with cataloging details, she composed objects — Catalog held indexes, LoanManager tracked due dates.
class Catalog:
def __init__(self):
self._items = {}
def add(self, item):
self._items[item.id] = item
def find_by_title(self, title):
return [i for i in self._items.values() if i.title == title]
class LoanManager:
def __init__(self):
self._loans = {} # item_id -> due_date
def borrow(self, item, days=14):
if not item.is_available():
raise RuntimeError("Not available")
item.check_out()
self._loans[item.id] = date.today() + timedelta(days=days)
def due_date(self, item):
return self._loans.get(item.id)
Composition kept responsibilities separated and testable.
2.2 Custom Descriptor for Validation
class PositiveNumber: def __set_name__(self, owner, name): self.name = namedef __get__(self, instance, owner): return instance.__dict__.get(self.name) def __set__(self, instance, value): if value <= 0: raise ValueError(f"self.name must be positive") instance.__dict__[self.name] = valueclass Order: quantity = PositiveNumber() price = PositiveNumber()
def __init__(self, quantity, price): self.quantity = quantity self.price = price
Now, Order(10, -5) raises ValueError: price must be positive.