Skip to content
English
  • There are no suggestions because the search field is empty.

Python 3 Deep Dive Part 4 Oop High Quality ◆

Let’s combine __slots__, property, descriptors, and __init_subclass__ into a small data validation framework:

class Validator:
    def __set_name__(self, owner, name):
        self.name = name
def __get__(self, obj, objtype=None):
    return obj.__dict__.get(self.name)
def __set__(self, obj, value):
    self.validate(value)
    obj.__dict__[self.name] = value
def validate(self, value):
    raise NotImplementedError

class Positive(Validator): def validate(self, value): if value <= 0: raise ValueError(f"self.name must be positive")

class NonEmptyString(Validator): def validate(self, value): if not isinstance(value, str) or len(value.strip()) == 0: raise ValueError(f"self.name must be non-empty string")

class Model: def init_subclass(cls, **kwargs): super().init_subclass(**kwargs) if not hasattr(cls, 'slots'): cls.slots = tuple()

def __repr__(self):
    attrs = k: getattr(self, k) for k in self.__slots__ if hasattr(self, k)
    return f"self.__class__.__name__(attrs)"

class Product(Model): slots = ('name', 'price') name = NonEmptyString() price = Positive()

p = Product() p.name = "Laptop" p.price = 999 print(p) # Product('name': 'Laptop', 'price': 999)

This pattern gives validation, memory efficiency, and clean introspection — a practical blend of several deep OOP features. python 3 deep dive part 4 oop high quality


class Engine: def start(self): ...

class Car: def init(self, engine: Engine): self._engine = engine def drive(self): self._engine.start()

The magic of Python objects lies in "dunder" (double underscore) methods. These allow objects to integrate seamlessly with the language's syntax.

__init__ initializes an already-created instance. __new__ actually creates the instance. It’s a static method (though not decorated as such) that receives the class and returns a new instance.

Use case examples:

class Singleton:
    _instance = None
    def __new__(cls, *args, **kwargs):
        if cls._instance is None:
            cls._instance = super().__new__(cls)
        return cls._instance

Welcome to Part 4 of our Python 3 Deep Dive series. If you’ve made it this far, you already understand control flow, functions, and iterables. Now, it’s time to tackle the paradigm that separates “scripting” from “software engineering”: Object-Oriented Programming (OOP).

But this is not your average “classes and objects” tutorial. This is a high-quality deep dive. We will explore Python’s OOP internals, metaclasses, descriptors, advanced method resolution, and the subtle patterns that lead to maintainable, production-grade code. Let’s combine __slots__ , property , descriptors, and


Python does not have true private attributes. Instead:

class Secret:
    def __init__(self):
        self.__code = 123

s = Secret() print(s.__code) # AttributeError print(s._Secret__code) # 123 — still accessible!

Name mangling exists primarily to avoid accidental overrides in subclasses, not to enforce privacy.

Python’s ethos: “We’re all consenting adults.” Use _ and trust others to respect it.


A metaclass is to a class what a class is to an instance. The default metaclass is type.

def my_meta(name, bases, dct):
    dct['version'] = 1.0
    return type(name, bases, dct)

class MyClass(metaclass=my_meta): pass

print(MyClass.version) # 1.0

Practical use: Singleton metaclass:

class SingletonMeta(type):
    _instances = {}
    def __call__(cls, *args, **kwargs):
        if cls not in cls._instances:
            cls._instances[cls] = super().__call__(*args, **kwargs)
        return cls._instances[cls]

class Database(metaclass=SingletonMeta): pass

When to use metaclasses (rarely!):

High-quality rule: If you’re unsure, don’t use metaclasses. They make code magical and hard to debug. Prefer class decorators. class Product(Model): slots = ('name', 'price') name =


| Anti-Pattern | Why It’s Bad | High-Quality Alternative | |--------------|---------------|---------------------------| | God Object | One class does everything | SRP + decomposition | | Circular imports | Two classes import each other | Move shared code to a third module | | Overusing inheritance | “Is-a” forced where “has-a” fits | Composition | | Mutable defaults | def __init__(self, items=[]) | def __init__(self, items=None) | | Using __slots__ prematurely | Optimizes nothing, restricts future | Only after profiling memory |