Metaclasses
Metaclasses
Section titled “Metaclasses”What it is
Section titled “What it is”A metaclass is “the class of a class.” Just as an object is created by calling its class, a class is created by calling its class — the metaclass. By default every class’s metaclass is type; writing a custom metaclass lets you hook into and modify class creation itself (adding methods, validating structure, registering subclasses) before any instance ever exists.
Before this feature
Section titled “Before this feature”Without a metaclass, enforcing a rule across every class in a family (e.g. “every subclass must define a name attribute”) has to be checked manually, or not at all — nothing stops a subclass from forgetting it until it fails at runtime:
class Plugin: def __init__(self): if not hasattr(self, "name"): raise TypeError("Plugin subclasses must define 'name'")
class Broken(Plugin): pass # no error until you actually instantiate it
# Broken() # fails here, not at class-definition timeAfter this feature
Section titled “After this feature”class PluginMeta(type): def __new__(mcs, name, bases, namespace): cls = super().__new__(mcs, name, bases, namespace) if bases and "name" not in namespace: # skip the base class itself raise TypeError(f"{name} must define 'name'") return cls
class Plugin(metaclass=PluginMeta): pass
class Broken(Plugin): pass # TypeError raised immediately, at class-definition time# type(x) tells you an object's class; type(SomeClass) tells you its metaclassclass Point: pass
print(type(Point())) # <class '__main__.Point'>print(type(Point)) # <class 'type'> -- every class's default metaclassWhy this is better
Section titled “Why this is better”- Enforced at definition time: broken subclasses fail as soon as the class is defined, not when someone eventually instantiates it.
- Framework-level hooks: Django’s ORM (
models.Model), SQLAlchemy’s declarative base, andabc.ABCMetaall use metaclasses to auto-register fields, wire up table mappings, or enforce abstract methods — behavior that has to happen once per class, not per instance. - Centralizes cross-cutting class logic: instead of repeating a check in every
__init__, it lives in one place that runs for every subclass automatically.
Key notes / edge cases
Section titled “Key notes / edge cases”class Foo(metaclass=Meta):is what triggersMeta.__new__/Meta.__init__instead of the defaulttype.__new__.- Metaclasses are rare in application code — reach for a class decorator or
__init_subclass__first; both solve most “do something when a class is defined” problems with far less complexity. __init_subclass__(Python 3.6+) covers the most common metaclass use case (validating/registering subclasses) without writing a metaclass at all:
class Plugin: def __init_subclass__(cls, **kwargs): super().__init_subclass__(**kwargs) if "name" not in cls.__dict__: raise TypeError(f"{cls.__name__} must define 'name'")
class Broken(Plugin): pass # TypeError, no metaclass needed- A common interview gotcha: “everything in Python is an object” extends to classes themselves — a class is an instance of its metaclass, which is itself an instance of
type(andtypeis an instance of itself).
Quick practice
Section titled “Quick practice”-
What is
type(SomeClass)for an ordinary class with no custom metaclass?Answer
`type` — every class's default metaclass, unless one is explicitly specified. -
What’s the simplest built-in alternative to a custom metaclass for validating subclasses?
Answer
`__init_subclass__`, defined on the base class — it runs automatically whenever a subclass is created, without needing a separate metaclass. -
Why do ORMs like Django’s models commonly use metaclasses?
Answer
To process class-level field declarations (e.g. `name = CharField()`) into database schema/mapping information once, at class-definition time, rather than repeating that work on every instance.