Metaclasses and __init_subclass__ in Python: Customising Class Creation
- A class statement is itself executed code: it builds a namespace, then calls a metaclass (
typeby default) to produce the class object. - A custom metaclass overrides
__new__or__init__ontypeto inspect or modify every class built from it. __init_subclass__hooks into the same moment without the ceremony of a separate metaclass class — it's defined directly on the base class.- Reach for a metaclass only when
__init_subclass__and class decorators genuinely can't do the job; most "I need a metaclass" problems don't.
What happens when a class statement runs
A class statement isn't declarative the way it looks — it's executed. Python runs the class body as code, collecting the names it defines (methods, class attributes) into a namespace, then calls a callable to turn that namespace into an actual class object. That callable is the metaclass. For an ordinary class with no explicit metaclass, this happens invisibly and the result is what you'd expect: a class object bound to the name you wrote after class.
type as the default metaclass
type is both the function you call to ask an object's type, and the default metaclass every ordinary class is built from. This makes the connection between class statements and type visible — you can build an equivalent class either way:
class Point:
def __init__(self, x, y):
self.x, self.y = x, y
# equivalent, built by calling type() directly:
Point2 = type("Point", (), {"__init__": lambda self, x, y: (setattr(self, "x", x), setattr(self, "y", y))})
Nobody writes classes the second way in practice — it's shown here purely to make the mechanism concrete: class Point: ... desugars into a call to a metaclass (type, unless told otherwise) with the class name, base classes, and namespace dict as arguments.
Writing a custom metaclass
A custom metaclass subclasses type and overrides __new__ or __init__ to run code at the moment any class using it is defined — not when an instance is created, but when the class itself comes into existence:
class ValidatingMeta(type):
def __new__(mcs, name, bases, namespace):
if "required_field" not in namespace and bases:
raise TypeError(f"{name} must define 'required_field'")
return super().__new__(mcs, name, bases, namespace)
class Base(metaclass=ValidatingMeta):
required_field = None
class Good(Base):
required_field = "ok"
class Bad(Base): # raises TypeError immediately, at class definition time
pass
The check fires when Bad is defined, not when it's instantiated — a whole class of mistakes gets caught at import time instead of surfacing later as a runtime AttributeError deep in some unrelated code path. This is genuinely useful, and it's also the source of most people's first confusing encounter with a metaclass conflict error when two base classes use different, incompatible metaclasses.
__init_subclass__: a lighter alternative
Python 3.6 added __init_subclass__, a classmethod-like hook defined directly on a normal base class, called automatically whenever a subclass is created — no separate metaclass class required:
class Base:
def __init_subclass__(cls, **kwargs):
super().__init_subclass__(**kwargs)
if "required_field" not in cls.__dict__:
raise TypeError(f"{cls.__name__} must define 'required_field'")
class Good(Base):
required_field = "ok"
class Bad(Base): # raises TypeError, same as the metaclass version
pass
This produces the identical validation behaviour as the metaclass example above, but the code lives on Base itself rather than in a separate ValidatingMeta class, and it composes more predictably with multiple inheritance since it doesn't introduce a second metaclass into the mix that other base classes' metaclasses might conflict with. It builds on the same subclassing concepts covered in the inheritance and polymorphism guide, just one level up from instances to classes themselves.
When to reach for either (and when not to)
In practice, most problems people reach for a metaclass to solve are better solved by __init_subclass__, a class decorator, or plain composition. A metaclass is the right tool specifically when you need to intercept the namespace dict before the class object exists — reordering attributes, rejecting certain names outright, or controlling which base classes are even allowed together, none of which __init_subclass__ can do since it runs after the class object already exists. Frameworks like Django's ORM and abstract base classes (see the abstract base classes guide, which itself uses a metaclass internally) are legitimate, well-established uses. For application code, treat a metaclass as a last resort, not a starting point — the Python data model documentation covers the full class-creation sequence in more depth than fits here, including how __prepare__ lets a metaclass control the namespace before the class body even executes.