Python 3 Deep Dive Part 4 Oop High Quality Instant

Most developers know __init__, but the real constructor is __new__.

Example: Singleton pattern using __new__:

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

s1 = Singleton() s2 = Singleton() print(s1 is s2) # True

Why does this matter for high-quality code?
Overriding __new__ allows you to control instance creation (e.g., caching, pooling, immutables). Never mutate __new__ without good reason, but understand it.


The dynamic nature of __dict__ consumes significant memory. If you are creating millions of instances, you can optimize memory by using __slots__. This tells Python to use a static array for attributes instead of a dynamic dictionary.

class Optimized:
    __slots__ = ['x', 'y']  # Fixed attribute set
def __init__(self, x, y):
        self.x = x
        self.y = y
o = Optimized(1, 2)
o.z = 3  # Raises AttributeError! Cannot add new attributes.

Trade-off: You save memory but lose the ability to dynamically add attributes. python 3 deep dive part 4 oop high quality


OOP’s greatest power is also its greatest danger: inheritance. High-quality OOP strictly follows the Liskov Substitution Principle: derived classes must be substitutable for their base classes without altering correctness.

In Python, everything is an object. Every integer, function, class, and module is an object allocated on the heap. Each object has:

class MyClass:
    pass

obj = MyClass() print(type(obj)) # <class 'main.MyClass'> print(type(MyClass)) # <class 'type'> – yes, classes are objects too! Most developers know __init__ , but the real

Understanding this is crucial: classes are runtime objects, not just compile-time blueprints.