The Story That Explains Metaclasses
Layer 1 — the cookies. Individual cookies on a tray. Each one is a real object you can eat. These are your instances (like
my_dog).Layer 2 — the cookie cutters. Metal shapes — star, heart, moon. Each cutter stamps out many cookies of one shape. These are your classes (like
Dog).Layer 3 — the cookie-cutter factory. The machine at the back of the workshop that manufactures the cutters themselves. Every cutter — star, heart, moon — was produced by this one machine. This is a metaclass (like
type).Now the twist. In Python, everything is an object — including classes. If a class is an object, something must have created it. That "something" is a metaclass. Python's default metaclass is
type — the machine that makes every class you've
ever written, from Dog to Vector to BankAccount.Write your own metaclass, and you customise how classes themselves get created. Add validation that every subclass must follow. Auto-register every plugin the moment it's defined. Inject methods into every class in a hierarchy. It's the deepest hook Python gives you.
Classes create objects. Metaclasses create classes. Just as an object's behaviour is defined
by its class, a class's behaviour is defined by its metaclass. Python's default metaclass
type is invisible until you replace it with your own — then every class
that uses your metaclass goes through your custom creation logic.
Everything Is an Object — Including Classes
Python has one central truth that unlocks metaclasses: classes are objects too.
You can pass them around, store them in lists, assign new attributes to them, and — the key part
— they were themselves created by another class. That creator class is
type.
class Dog:
pass
buddy = Dog()
# buddy is an instance of Dog
print(type(buddy)) # <class '__main__.Dog'>
# Dog is an instance of type — that's the metaclass level
print(type(Dog)) # <class 'type'>
# type is itself an instance of type — the recursion stops here
print(type(type)) # <class 'type'>
# Classes have __class__ pointing to their metaclass
print(Dog.__class__) # <class 'type'>
Animated Diagram — The Three-Level Hierarchy
Trace the "what is it an instance of?" arrow at each level. The individual dog is an instance
of the Dog class. The Dog class is an instance of type.
type is an instance of itself — the top of the chain.
Every object has a class. Every class has a metaclass. type is where the recursion stops — it's the metaclass of itself.
Creating a Class Dynamically With type()
Because type is the metaclass, calling it with the right arguments
creates a class from scratch — no class keyword needed. This is
the machinery Python uses internally every time you write a class.
# These two forms produce IDENTICAL classes — Python translates one into the other
# Form 1 — the class keyword you already know
class Dog:
species = "Canis familiaris"
def bark(self):
return f"{self.species} says woof"
# Form 2 — same thing, built by calling type() directly
def bark(self):
return f"{self.species} says woof"
Dog2 = type(
"Dog2", # class name
(), # base classes
{"species": "Canis familiaris", "bark": bark} # namespace
)
# Both work identically
print(Dog().bark()) # Canis familiaris says woof
print(Dog2().bark()) # Canis familiaris says woof
print(type(Dog) is type(Dog2)) # True — both classes have type() as their metaclass
type
When called with three arguments, type creates a class:
name (string), bases (tuple of parent classes), and
namespace (dict of attributes and methods). Every class you've ever written
with the class keyword ends up as this same call under the hood.
Your First Custom Metaclass
To write a metaclass, subclass type. Override its __new__ or
__init__ to run code every time a class using your metaclass is defined.
Then attach it via metaclass=YourMeta in the class header.
class LoggingMeta(type):
"""A metaclass that announces every class defined with it."""
def __new__(mcs, name, bases, namespace):
print(f"[LoggingMeta] creating class {name!r} with {len(namespace)} attrs")
# Delegate the real creation to type
cls = super().__new__(mcs, name, bases, namespace)
# Free to modify the class here — inject methods, add attributes...
cls.created_at = "2026-07-12"
return cls
class Cat(metaclass=LoggingMeta):
def meow(self):
return "meow"
class Dog(metaclass=LoggingMeta):
def bark(self):
return "woof"
# The metaclass injected created_at into every class
print(Cat.created_at) # 2026-07-12
print(Dog.created_at) # 2026-07-12
# type() confirms the metaclass
print(type(Cat)) # <class '__main__.LoggingMeta'>
print(type(Dog)) # <class '__main__.LoggingMeta'>
Animated Diagram — The Metaclass Intercepts Class Creation
Watch what happens when Python encounters class Dog(metaclass=LoggingMeta):.
Before the class object even exists, Python calls LoggingMeta.__new__. Your
metaclass sees the raw name, bases, and namespace — and can validate, decorate, or reject before
handing back the finished class.
The metaclass runs at class definition time — before any instances exist. It's your one chance to shape the class itself.
What Do Metaclass __new__, __init__, and __call__ Control?
mcs, name, bases, namespace.
Return the class. Best place to validate the namespace or
inject attributes before the class is finalised.
__new__. Receives the created class as cls. Best
place to register the class in a global registry or perform post-processing.
MyClass(...). Controls how instances are created —
this is what powers metaclass singletons and instance caches.
class Meta(type):
def __new__(mcs, name, bases, namespace):
print(f" [Meta.__new__] creating class {name!r}")
return super().__new__(mcs, name, bases, namespace)
def __init__(cls, name, bases, namespace):
print(f" [Meta.__init__] class {name!r} is ready")
super().__init__(name, bases, namespace)
def __call__(cls, *args, **kwargs):
print(f" [Meta.__call__] creating an instance of {cls.__name__}")
return super().__call__(*args, **kwargs)
print("Defining Dog:")
class Dog(metaclass=Meta):
def __init__(self, name):
self.name = name
print("\nCreating buddy:")
buddy = Dog("Buddy")
Meta.__new__ and Meta.__init__ run once per class
— the moment Python encounters the class statement. Meta.__call__
runs every time you instantiate that class. This is the difference between
"shape the class" and "shape each instance."
Practical Example 1 — Enforcing Class-Design Rules
A metaclass that validates every class it produces. Here we enforce that every public method's name must be lowercase — a small stand-in for real coding standards you might want to enforce across a codebase.
class SnakeCaseMeta(type):
"""Every public method must be snake_case (lowercase)."""
def __new__(mcs, name, bases, namespace):
for attr_name, attr_value in namespace.items():
# Only check callable, public attributes
if callable(attr_value) and not attr_name.startswith("_"):
if not attr_name.islower():
raise TypeError(
f"In class {name!r}: method {attr_name!r} "
"must be lowercase (snake_case)"
)
return super().__new__(mcs, name, bases, namespace)
# ── This class is fine — all methods are snake_case ──
class Good(metaclass=SnakeCaseMeta):
def do_something(self):
return "ok"
def save_to_disk(self):
return "saved"
print("Good class created successfully")
# ── This class violates the rule — refused at DEFINITION time ──
try:
class Bad(metaclass=SnakeCaseMeta):
def DoSomething(self): # PascalCase — not allowed
return "nope"
except TypeError as e:
print("Blocked:", e)
The check runs the instant Python parses the class body — before any code that uses the class ever runs. It's not a linter you have to remember to invoke; it's structural enforcement built into the class system. This is how frameworks like Django and SQLAlchemy enforce their conventions.
Practical Example 2 — Auto-Registry of Subclasses
A metaclass that quietly keeps track of every subclass ever defined. Add a new plugin, and it auto-registers itself — no manual bookkeeping. This is the pattern behind plugin systems, ORM model discovery, serializer registries, and much more.
class PluginRegistry(type):
"""A metaclass that auto-registers every subclass into a shared registry."""
registry = {} # class-level dict — one for the entire metaclass
def __init__(cls, name, bases, namespace):
super().__init__(name, bases, namespace)
# Don't register the base "Plugin" itself — only its children
if bases:
PluginRegistry.registry[name] = cls
print(f"[registry] Registered {name}")
# The base class — not registered because it has no bases
class Plugin(metaclass=PluginRegistry):
def run(self, msg):
raise NotImplementedError
# Every subclass is automatically added to the registry — no manual work
class EmailPlugin(Plugin):
def run(self, msg):
return f"EMAIL: {msg}"
class SMSPlugin(Plugin):
def run(self, msg):
return f"SMS: {msg}"
class PushPlugin(Plugin):
def run(self, msg):
return f"PUSH: {msg}"
# The registry now contains every plugin — dispatch by name
print()
print("All registered plugins:", list(PluginRegistry.registry))
for plugin_name, plugin_cls in PluginRegistry.registry.items():
plugin = plugin_cls()
print(f" {plugin_name} → {plugin.run('hello')}")
Animated Diagram — The Registry Grows With Each Subclass
Every subclass triggers PluginRegistry.__init__ and adds itself. No decorator, no manual registration — the metaclass does it automatically.
The Modern Alternative — __init_subclass__
Python 3.6 introduced __init_subclass__, which covers most real use cases
for metaclasses without the ceremony. If all you need is "run some code every time a
subclass is defined," this is almost always the better choice.
# ── SAME auto-registry pattern — no metaclass needed ──────
class Plugin:
registry = {}
def __init_subclass__(cls, **kwargs):
super().__init_subclass__(**kwargs)
Plugin.registry[cls.__name__] = cls
print(f"[registry] Registered {cls.__name__}")
class EmailPlugin(Plugin):
def run(self, msg): return f"EMAIL: {msg}"
class SMSPlugin(Plugin):
def run(self, msg): return f"SMS: {msg}"
print(list(Plugin.registry)) # ['EmailPlugin', 'SMSPlugin']
| Requires understanding of the metaclass concept |
| Verbose: name, bases, namespace parameters |
| Metaclass conflicts when mixing classes |
| Powerful: full control over class creation |
| Right choice for framework authors |
| Just a method on the base class |
Takes cls, **kwargs — minimal |
| No metaclass conflicts |
| Sufficient for 90% of real needs |
| Right choice for application authors |
"Metaclasses are deeper magic than 99% of users should ever worry about. If you wonder
whether you need them, you don't." — Tim Peters. Try __init_subclass__ or a
simple class decorator first. Reach for a metaclass only when those genuinely don't fit —
typically when you need to control the class namespace itself before the class is
built.
When to Use a Metaclass (and When Not To)
__init_subclass__ instead. Same behaviour, none of the
metaclass complexity, no chance of metaclass conflicts when mixing with other hierarchies.__new__
on the class. A metaclass just to make one class singleton is overkill.Common Mistakes (and Fixes)
| Mistake | What Goes Wrong | Fix |
|---|---|---|
Forgetting to return from Meta.__new__ |
The class definition evaluates to None |
Always end __new__ with return cls (the created class) |
Using self instead of mcs / cls |
Confusing. In __new__, first arg is mcs (the metaclass);
in __init__/__call__, it's cls (the class being created) |
Match convention: def __new__(mcs, name, bases, ns), def __init__(cls, ...) |
Modifying namespace then not passing the modified version to super().__new__ |
Your changes are silently dropped | Pass the same (or updated) namespace: super().__new__(mcs, name, bases, namespace) |
| Metaclass conflict in multiple inheritance | TypeError: metaclass conflict when parents have different metaclasses |
Choose one metaclass that's a subclass of the others, or use __init_subclass__ instead |
Putting logic in Meta.__init__ that needs pre-class-creation info |
The class already exists — some kinds of modification are too late | Do namespace-level work in __new__. Reserve __init__ for post-processing |
| Registering the base class alongside subclasses | The registry contains the abstract base too — polluting downstream lookups | Check if bases: — only register when there's a real parent (not the base itself) |
| Reaching for a metaclass when a class decorator would do | Unnecessary complexity that other developers must decode | Prefer decorators, __init_subclass__, or plain composition. Metaclass is the last resort |
Quick Reference
| Task | Syntax | Notes |
|---|---|---|
| Default metaclass | type | Every class not otherwise marked uses type |
| Get an object's class | type(obj) | Same as obj.__class__ |
| Get a class's metaclass | type(SomeClass) | Same as SomeClass.__class__ |
| Create class dynamically | type(name, bases, namespace) | Three-argument form of type() |
| Custom metaclass | class MyMeta(type): ... | Subclass type |
| Attach to a class | class C(metaclass=MyMeta): | Keyword in class header |
| Intercept class creation | def __new__(mcs, name, bases, ns): | Runs once per class definition |
| Post-process a class | def __init__(cls, ...): | Class exists here; add to registries, etc. |
| Intercept instantiation | def __call__(cls, *a, **kw): | Runs every time MyClass(...) is called |
| Modern alternative | def __init_subclass__(cls, **kw): | No metaclass needed for most cases |
Golden Rules
type. Understanding this one sentence unlocks the entire metaclass system.
type with your own __new__,
__init__, or __call__ methods.
Subclass type, add the hooks you need, attach with metaclass=.
Meta.__new__ runs before the class exists — best place for
namespace validation or attribute injection. Meta.__init__ runs
after — best place for registration and post-processing.
Meta.__call__ runs on every instantiation — controls how instances are made.
__init_subclass__ when all you need is "do something for
every subclass." It covers 90% of real metaclass use cases with none of the complexity, and
never causes metaclass conflicts.
mcs as the first parameter of
__new__ (metaclass "self"), and cls for
__init__/__call__ (the class being processed). It matches what
every experienced Python developer expects.
__init__, check if bases:
first — otherwise the abstract base class registers itself too, polluting your lookup table.
__init_subclass__. Do I want to transform one specific class?
→ class decorator. Reach for the simplest tool that solves the problem.