Metaclasses, Descriptors & C3 Linearization (MRO)
Master Python's deepest object model internals: intercepting class creation with Metaclasses (`__new__`, `__init__`), building validated fields with the Descriptor Protocol (`__get__`, `__set__`, `__set_name__`), and understanding C3 Linearization (Method Resolution Order).
What You Will Learn in This Lesson
- How classes are themselves instances of metaclasses (`type`)
- The Descriptor Protocol: `__get__`, `__set__`, `__delete__`, and `__set_name__`
- Building an ORM field validator using descriptors and `__init_subclass__`
- Method Resolution Order (MRO) calculation using the C3 Linearization algorithm
Introduction & Core Concept
Descriptors power Python's built-in `@property`, `@classmethod`, and `@staticmethod` decorators. Understanding descriptors allows you to write reusable attribute validation engines with zero boilerplate.
Syntax & Structure
class TypedField: def __set_name__(self, owner, name): self.name = name def __set__(self, instance, value): ... class Meta(type): def __new__(mcs, name, bases, attrs): ...Type-Safe Model Architecture with Descriptors and Metaclasses
python1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556# Metaprogramming with Descriptors and Metaclasses# 1. Type-Enforcing Descriptor Protocolclass ValidatedString:def __init__(self, min_len: int = 1, max_len: int = 100):self.min_len = min_lenself.max_len = max_lendef __set_name__(self, owner, name):# Automatically captures the attribute name on the owner classself.storage_name = f"_{name}"def __get__(self, instance, owner):if instance is None:return selfreturn getattr(instance, self.storage_name, "")def __set__(self, instance, value):if not isinstance(value, str):raise TypeError(f"Attribute must be a string, got {type(value).__name__}")if not (self.min_len <= len(value) <= self.max_len):raise ValueError(f"Length must be between {self.min_len} and {self.max_len} chars.")setattr(instance, self.storage_name, value)# 2. Metaclass registering all managed domain entitiesclass ModelRegistryMeta(type):registry = {}def __new__(mcs, name, bases, attrs):cls = super().__new__(mcs, name, bases, attrs)if name != "BaseModel":mcs.registry[name] = clsprint(f"[Metaclass] Registered Model: {name}")return clsclass BaseModel(metaclass=ModelRegistryMeta):pass# 3. Clean Domain Class Definitionclass CourseEntity(BaseModel):title = ValidatedString(min_len=3, max_len=50)slug = ValidatedString(min_len=2, max_len=30)def __init__(self, title: str, slug: str):self.title = titleself.slug = slug# Demonstrationcourse = CourseEntity("Distributed Systems", "distributed-systems")print(f"✅ Created Course: {course.title} (Slug: {course.slug})")# Validations fire automatically on assignment!try:course.title = "" # Raises ValueErrorexcept ValueError as e:print(f"Validation Caught: {e}")
Line-by-Line Technical Breakdown
Try It Yourself (Interactive Editor)
Modify the code in real-time and click Run to test live browser output and console logs.
Common Mistakes & How to Avoid Them
#1: Storing descriptor values directly on the descriptor instance `self.val` instead of on the target object `instance`.
Descriptors are class attributes shared across all instances. Storing state on `self` shares data across every object.
def __set__(self, instance, value): self.val = value # Overwrites across ALL instances!def __set__(self, instance, value): setattr(instance, self.storage_name, value)Industry Best Practices & Professional Standards
- Use `__init_subclass__` for simple class initialization hooks instead of heavy metaclasses.
- Always implement `__set_name__` in custom descriptors for clean private attribute naming.
- Inspect `Class.__mro__` to debug complex diamond multiple inheritance hierarchies.
Lesson Summary & Core Takeaways
- Metaclasses customize the creation and registration of classes at import time.
- Descriptors intercept attribute get, set, and delete operations on instances.
- C3 Linearization guarantees deterministic multiple inheritance resolution.