Saltar a contenido

Week 5 - Object Model Deep Dive: Classes, Descriptors, Metaclasses

5.1 Conceptual Core

  • Attribute lookup is a protocol, not a field read. obj.x calls type(obj).__getattribute__(obj, "x"), which checks the data-descriptor chain on the type, then the instance dict, then non-data descriptors, then __getattr__.
  • Descriptors (__get__, __set__, __delete__) are how @property, staticmethod, classmethod, and __slots__ actually work. Understanding descriptors is understanding 80% of Python's "magic."
  • Metaclasses (type is the default) intercept class creation. They are over-used; __init_subclass__ (PEP 487) and class decorators cover most legitimate use cases.

5.2 Mechanical Detail

  • __slots__: replaces the per-instance __dict__ with a fixed-size struct of slot descriptors. Saves ~40–60% memory per instance and speeds attribute access. Cost: no dynamic attributes, multiple-inheritance gotchas. Mandatory in hot-path data classes.
  • MRO (method resolution order) and the C3 linearization: MyClass.__mro__. Diamond inheritance is solvable but signals over-design.
  • super(): a proxy object that walks the MRO of type(self) starting after the current class. Always cooperative; do not pass super() __init__ arguments unless you know the MRO.
  • @property, @cached_property (3.8+, requires writable __dict__ - incompatible with default __slots__ unless you slot __dict__).
  • __init_subclass__(cls, **kwargs) runs at subclass creation. Used for plugin registration, validation of subclass invariants. The 90%-case alternative to a metaclass.

5.3 Lab - "Build a Tiny ORM"

  1. Implement a Field descriptor with type validation and a default. class User: name = Field(str); age = Field(int, default=0).
  2. Use __init_subclass__ to collect declared fields into cls._fields. Auto-generate __init__ and __repr__.
  3. Compare your hand-rolled version to @dataclass(slots=True). Note where dataclass is better (PEP-595 ordering, __eq__, __hash__).
  4. Implement a RegistryMeta metaclass that records every subclass in a class-level dict. Then re-implement using __init_subclass__. Defend the simpler version in writing.

5.4 Idiomatic & Linter Drill

  • Enable ruff SLOT rule. Add __slots__ to every internal data class in your project. Note size delta with pympler.asizeof.

5.5 Production Hardening Slice

  • Add pyright strict mode. Add from __future__ import annotations and switch to PEP 604 union syntax (X | Y). Resolve all type errors.

Comments