Python 3 Deep Dive Part 4 Oop High Quality -

Let’s combine everything into a high-quality OOP design: a plugin architecture for a data processing pipeline.

from abc import ABC, abstractmethod
from typing import List, Dict, Any
import importlib

class Plugin(ABC): """Base class for all plugins.""" name: str

@abstractmethod
def process(self, data: Dict[str, Any]) -> Dict[str, Any]:
    pass

class Pipeline: def init(self): self._plugins: List[Plugin] = []

def register(self, plugin: Plugin) -> None:
    if not isinstance(plugin, Plugin):
        raise TypeError("Must be Plugin subclass")
    self._plugins.append(plugin)
def run(self, initial_data: Dict[str, Any]) -> Dict[str, Any]:
    data = initial_data
    for plugin in self._plugins:
        data = plugin.process(data)
    return data

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.


# Bad (inheritance)
class Engine:
    def start(self): ...

class Car(Engine): def drive(self): self.start() python 3 deep dive part 4 oop high quality

This is where Python diverges from static languages like C++ or Java.

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.


Why descriptors matter for high-quality code:

Without descriptors, you’d repeat property boilerplate everywhere.


In languages like Java, private attributes are accessed via getters/setters. In Python, we start with public attributes and refactor to properties when needed.

Bad (Java-style):

class BadCircle:
    def __init__(self, radius):
        self._radius = radius
    def get_radius(self):
        return self._radius
    def set_radius(self, value):
        if value < 0:
            raise ValueError("Radius cannot be negative")
        self._radius = value

Good (Pythonic):

class Circle:
    def __init__(self, radius):
        self.radius = radius  # Uses setter if defined
@property
def radius(self):
    return self._radius
@radius.setter
def radius(self, value):
    if value < 0:
        raise ValueError("Radius cannot be negative")
    self._radius = value
@property
def area(self):
    return 3.14159 * self._radius ** 2

Key insight: Properties allow you to evolve an attribute into logic without changing the API. Your users still write circle.radius = 5, not circle.set_radius(5).


Python has no real access control. We rely on naming conventions:

Name mangling example:

class Foo:
    def __init__(self):
        self.__secret = 42
    def get_secret(self):
        return self.__secret

f = Foo() print(f._Foo__secret) # 42 – still accessible, but harder to accidentally access

High-quality wisdom: Do not overuse __ (double underscore). It breaks subclassing. Use _ for internal attributes and trust other developers. Python is a "consenting adults" language.


},3000) $("#google_esf").attr("title","Ads"); });