What are metaclasses, and when (if ever) should you use one?
Quick Answer
A **metaclass** is "the class of a class" — by default every class's metaclass is `type`, but subclassing `type` and passing it via `class Foo(Base, metaclass=MyMeta):` lets you customize *how the class itself is constructed* (e.g., auto-registering subclasses, enforcing naming conventions, injecting methods). They're powerful but rarely needed in application code — most use cases are better served by `__init_subclass__`, class decorators, or plain composition.
Detailed Answer
Classes are instances of type
class Foo:
pass
type(Foo) # <class 'type'>
type(Foo()) # <class '__main__.Foo'>
Foo is itself an object — an instance of type. A metaclass is just a
class whose instances are classes, i.e., a subclass of type.
Writing a metaclass
class RegisteringMeta(type):
registry = {}
def __new__(mcs, name, bases, namespace):
cls = super().__new__(mcs, name, bases, namespace)
if name != "Base":
mcs.registry[name] = cls
return cls
class Base(metaclass=RegisteringMeta):
pass
class PluginA(Base):
pass
class PluginB(Base):
pass
RegisteringMeta.registry # {'PluginA': <class PluginA>, 'PluginB': <class PluginB>}
Every time a class is defined with metaclass=RegisteringMeta in its
hierarchy, RegisteringMeta.__new__ runs during class creation (not
instance creation) — this is how plugin auto-registration, ORMs
(Django models, SQLAlchemy declarative base), and enum-like machinery
work under the hood.
The lighter-weight alternative: __init_subclass__
class Base:
registry = {}
def __init_subclass__(cls, **kwargs):
super().__init_subclass__(**kwargs)
Base.registry[cls.__name__] = cls
class PluginA(Base):
pass
__init_subclass__ (PEP 487, Python 3.6+) covers the most common
metaclass use case — reacting to subclass creation — without the
complexity of writing a full metaclass, and it composes more predictably
with multiple inheritance since there's no metaclass-conflict problem to
worry about.
When a real metaclass is still justified
- You need to intercept class creation itself, not just subclassing (e.g., validating/transforming the class namespace before the class object even exists).
- You're building a framework-level abstraction (an ORM base class, an
ABC-like mechanism) where
__init_subclass__genuinely isn't expressive enough.
The famous caution
"Metaclasses are deeper magic than 99% of users should ever worry about... If you wonder whether you need them, you don't." — Tim Peters
Interview-ready summary: A metaclass customizes how classes themselves
are constructed by subclassing type; it's the mechanism behind ORMs and
plugin registries. For the common "do something when a subclass is
defined" need, prefer __init_subclass__ — reach for a full metaclass
only when you must control class creation, not just react to
subclassing.