What are metaclasses, and when (if ever) should you use one?

7 minadvancedoopmetaclassesadvanced

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. It also 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 — for example, 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

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.

Related Resources