Type something to search...
Metaclasses in Python Explained Without the Headache

Metaclasses in Python Explained Without the Headache

Metaclasses have a reputation. Tim Peters famously said they're "deeper magic than 99% of users should ever worry about", and most tutorials respond by either skipping them or burying you in __new__ signatures. The result is that a lot of experienced Python developers use metaclasses every day (every Enum, every ABC, every Django model) without a clear picture of what one is.

The core idea is short: a metaclass is the class of a class. Just as a class controls how its instances are created and behave, a metaclass controls how classes are created and behave. Everything else is detail.

In this post I'll build that idea up step by step: classes as objects, type as the default metaclass, exactly what happens when Python runs a class statement, how to write a metaclass, and a few realistic uses. Then I'll show the simpler tools (__init_subclass__ and class decorators) that have replaced most metaclasses in modern code, so you know when not to write one.

Step 1: Classes Are Objects

In Python, a class is an ordinary object created at runtime. You can pass it around, store it in a dict, and ask for its type:

class Dog:
    sound = "woof"

    def speak(self) -> str:
        return self.sound


d = Dog()
print(type(d), type(Dog), type(type))
print(isinstance(Dog, type))
<class '__main__.Dog'> <class 'type'> <class 'type'>
True

Read that carefully. d is an instance of Dog. Dog is an instance of type. So type is the class that classes are made from: it's the default metaclass. (And type is its own type, which is where the chain stops.)

Built-ins work the same way: type(int) and type(object) are both type.

Step 2: type Can Build Classes Directly

You know type(obj) as "tell me the class of this object". Called with three arguments, type does something else: it creates a new class.

def speak(self) -> str:
    return self.sound


Cat = type("Cat", (), {"sound": "meow", "speak": speak})
print(Cat, Cat().speak(), type(Cat))
<class '__main__.Cat'> meow <class 'type'>

The three arguments are the class name, a tuple of base classes, and a dictionary of attributes (the namespace). This is exactly what a class statement does under the hood. The class keyword is a friendlier syntax for calling a metaclass.

Step 3: What a class Statement Actually Does

When Python executes this:

class Widget(Base):
    color = "blue"

    def render(self) -> str:
        return self.color

it goes through these steps:

  1. Pick the metaclass. Use the metaclass= keyword if given; otherwise use the metaclass of the base classes (which is type for normal classes).
  2. Prepare a namespace. Call metaclass.__prepare__(name, bases) to get a mapping, usually an empty dict.
  3. Run the class body inside that namespace. Every assignment and def in the body becomes an entry: color, render, and a few extras like __module__ and __qualname__.
  4. Create the class. Call metaclass(name, bases, namespace), which runs the metaclass's __new__ and then its __init__.
  5. Bind the name. Assign the resulting class object to Widget in the enclosing scope (after applying any class decorators).

Later, when you create an instance with Widget(), that's a call on the class object, which means the metaclass's __call__ runs. type.__call__ is what invokes the class's own __new__ and __init__.

You can watch all of this by writing a metaclass that logs each hook:

class Meta(type):
    @classmethod
    def __prepare__(mcls, name, bases, **kwargs):
        print(f"1. __prepare__ for {name}")
        return {}

    def __new__(mcls, name, bases, namespace, **kwargs):
        user_attrs = sorted(k for k in namespace if not k.startswith("__"))
        print(f"3. __new__ creating {name} with {user_attrs}")
        return super().__new__(mcls, name, bases, namespace)

    def __init__(cls, name, bases, namespace, **kwargs):
        print(f"4. __init__ for {name}")
        super().__init__(name, bases, namespace)

    def __call__(cls, *args, **kwargs):
        print(f"5. __call__: creating an instance of {cls.__name__}")
        return super().__call__(*args, **kwargs)


class Widget(metaclass=Meta):
    print("2. class body runs")
    color = "blue"

    def render(self) -> str:
        return self.color


w = Widget()
print(w.render())
1. __prepare__ for Widget
2. class body runs
3. __new__ creating Widget with ['color', 'render']
4. __init__ for Widget
5. __call__: creating an instance of Widget
blue

Steps 1 to 4 happen once, when the module is imported and the class statement runs. Step 5 happens every time you create an instance.

A naming note: inside a metaclass, the first parameter of __new__ is the metaclass itself (conventionally mcls or mcs), and the first parameter of __init__ and other methods is the class being made (conventionally cls). The shift by one level is what makes metaclass code look confusing at first.

Metaclasses Are Inherited

Once a class has a metaclass, its subclasses get it too:

class Sub(Widget):
    pass
1. __prepare__ for Sub
3. __new__ creating Sub with []
4. __init__ for Sub

type(Sub) is Meta. This is what makes metaclasses powerful for frameworks: put the metaclass on one base class, and every class users derive from it is processed automatically.

Writing a Useful Metaclass: A Plugin Registry

A classic use is collecting every subclass into a registry, so a framework can look up implementations by name:

# plugins.py
class PluginMeta(type):
    registry: dict[str, type] = {}

    def __new__(mcls, name, bases, namespace, **kwargs):
        cls = super().__new__(mcls, name, bases, namespace, **kwargs)
        if bases:  # skip the base Plugin class itself
            key = namespace.get("name", name.lower())
            mcls.registry[key] = cls
        return cls


class Plugin(metaclass=PluginMeta):
    def run(self, text: str) -> str:
        raise NotImplementedError


class Upper(Plugin):
    def run(self, text: str) -> str:
        return text.upper()


class Reverse(Plugin):
    name = "rev"

    def run(self, text: str) -> str:
        return text[::-1]


print(PluginMeta.registry)
print(PluginMeta.registry["rev"]().run("hello"))
{'upper': <class '__main__.Upper'>, 'rev': <class '__main__.Reverse'>}
olleh

Defining a subclass is enough to register it. No decorator to forget, no central list to update.

Other Things Metaclasses Can Do

Control Instance Creation with __call__

Because Widget() calls the metaclass's __call__, a metaclass can change what instantiation means. The common (if overused) example is a singleton:

class Singleton(type):
    _instances: dict[type, object] = {}

    def __call__(cls, *args, **kwargs):
        if cls not in cls._instances:
            cls._instances[cls] = super().__call__(*args, **kwargs)
        return cls._instances[cls]


class Config(metaclass=Singleton):
    def __init__(self) -> None:
        print("loading config")
        self.debug = False


a = Config()
b = Config()
print(a is b)
loading config
True

__init__ runs only once. In practice, a module-level instance (config = Config()) is usually the simpler way to get one shared object in Python, but this shows the hook.

Customise the Namespace with __prepare__

__prepare__ lets you supply the mapping the class body executes in. That means you can observe every assignment as it happens. For example, rejecting a method that's accidentally defined twice (normally the second silently replaces the first):

class NoDuplicates(dict):
    def __setitem__(self, key, value):
        if key in self and not key.startswith("__"):
            raise TypeError(f"{key!r} defined twice")
        super().__setitem__(key, value)


class StrictMeta(type):
    @classmethod
    def __prepare__(mcls, name, bases, **kwargs):
        return NoDuplicates()

    def __new__(mcls, name, bases, namespace, **kwargs):
        return super().__new__(mcls, name, bases, dict(namespace), **kwargs)


class Handlers(metaclass=StrictMeta):
    def on_save(self): ...
    def on_load(self): ...
    def on_save(self): ...
# TypeError: 'on_save' defined twice

__prepare__ is the one hook that has no simpler replacement. If you need to see the class body while it runs, a metaclass is the only way. The standard library's Enum uses this to detect duplicate member names.

Add Methods to the Class Itself

Methods defined on a metaclass are available on the class, but not on its instances:

class Meta(type):
    def describe(cls) -> str:
        return f"class {cls.__name__}"


class Thing(metaclass=Meta):
    pass


print(Thing.describe())  # class Thing
Thing().describe()       # AttributeError: 'Thing' object has no attribute 'describe'

That's how len(MyEnum) and for member in MyEnum work: EnumType, the metaclass of Enum, defines __len__ and __iter__ for enum classes. (See Enums in Python for the user-facing side.)

Metaclasses You Already Use

import abc
import enum

print(type(enum.Enum), type(abc.ABC))
<class 'enum.EnumType'> <class 'abc.ABCMeta'>
  • ABCMeta collects abstract methods when the class is created and blocks instantiation in __call__ if any remain. The abc module post covers it.
  • EnumType turns class attributes into member singletons, prevents reassignment, and makes the class iterable.
  • ORMs like Django use a metaclass (ModelBase) to turn field declarations into database columns and build the objects manager. SQLAlchemy's declarative base has used both metaclass and __init_subclass__-based approaches over its history.

The Modern Alternatives

Most of what people used metaclasses for before Python 3.6 now has a simpler tool. Reach for these first.

__init_subclass__

Python 3.6 added __init_subclass__, a class method on the parent that runs every time a subclass is created. Here's the plugin registry again, with no metaclass:

# plugins.py
class Plugin:
    registry: dict[str, type["Plugin"]] = {}

    def __init_subclass__(cls, /, name: str | None = None, **kwargs) -> None:
        super().__init_subclass__(**kwargs)
        Plugin.registry[name or cls.__name__.lower()] = cls

    def run(self, text: str) -> str:
        raise NotImplementedError


class Upper(Plugin):
    def run(self, text: str) -> str:
        return text.upper()


class Reverse(Plugin, name="rev"):
    def run(self, text: str) -> str:
        return text[::-1]


print(Plugin.registry)
{'upper': <class '__main__.Upper'>, 'rev': <class '__main__.Reverse'>}

It's shorter, it doesn't introduce a new type, and keyword arguments in the class statement (name="rev") are passed straight to it. It also works fine alongside other base classes, which metaclasses often don't (see below).

It's also a good place for definition-time validation:

class Command:
    def __init_subclass__(cls, **kwargs):
        super().__init_subclass__(**kwargs)
        if not callable(getattr(cls, "execute", None)):
            raise TypeError(f"{cls.__name__} must define execute()")
        if not cls.__doc__:
            raise TypeError(f"{cls.__name__} needs a docstring for --help")


class Deploy(Command):
    """Deploy the current build."""

    def execute(self) -> None: ...


class Broken(Command):
    def execute(self) -> None: ...
# TypeError: Broken needs a docstring for --help

Class Decorators

If you only need to process specific classes, not a whole hierarchy, a class decorator is a plain function that receives the finished class and returns it (or a replacement):

def register(registry: dict):
    def decorator(cls):
        registry[cls.__name__.lower()] = cls
        return cls
    return decorator


HANDLERS: dict[str, type] = {}


@register(HANDLERS)
class Csv: ...


@register(HANDLERS)
class Json: ...


print(HANDLERS)  # {'csv': <class '__main__.Csv'>, 'json': <class '__main__.Json'>}

@dataclass is the best-known class decorator. It reads the class's annotations and generates __init__, __repr__, and friends, all without a metaclass. Decorators are explicit (you can see them on the class) and aren't inherited, which is often what you want. For more on how decorators work in general, see Python Decorators Explained.

__set_name__

If you're tempted to write a metaclass so that attributes know their own names, __set_name__ on the attribute's class already does that. The descriptors post shows it in action.

Why Metaclasses Cause Trouble: Conflicts

A class can have only one metaclass, and it must be compatible with the metaclasses of all its bases. Mixing two unrelated metaclasses fails:

class MetaA(type): pass
class MetaB(type): pass

class A(metaclass=MetaA): pass
class B(metaclass=MetaB): pass

class C(A, B): pass
TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases

The fix is a combined metaclass, class MetaAB(MetaA, MetaB): pass, used as class C(A, B, metaclass=MetaAB). That works when the two metaclasses cooperate, but it's fragile, and it's a real problem when the metaclasses come from two libraries (say, an ORM base class and ABC). This is the biggest practical reason to prefer __init_subclass__, which has no such conflicts.

When Should You Write a Metaclass?

A short decision list:

You want to...Use
Run code whenever a subclass is defined__init_subclass__
Validate or register subclasses__init_subclass__
Modify or wrap one specific classA class decorator
Let an attribute know its own name__set_name__
Generate methods from annotationsA class decorator (like @dataclass)
Observe or control the class body as it executesA metaclass (__prepare__)
Change what MyClass() returns for a whole hierarchyA metaclass (__call__), or a factory function
Give classes themselves operators or methods (len(MyClass))A metaclass

If your need is in the first five rows, you don't need a metaclass. The last three are where metaclasses are still the right tool, and they mostly come up when you're building a framework or a library others will subclass.

Conclusion

A metaclass is the class of a class. type is the default one, and a class statement is essentially a call to it: prepare a namespace, run the body, then create the class with __new__ and __init__. Instantiating that class later goes through the metaclass's __call__. Write your own metaclass by subclassing type and overriding those hooks.

That's genuinely all there is to it. The headache comes from overuse, not complexity. For registering subclasses, validating definitions, or adding generated methods, __init_subclass__ and class decorators do the job with less code and no metaclass conflicts. Keep metaclasses for the rare cases that need __prepare__, class-level operators, or control over instantiation across a whole hierarchy, and you'll recognise them easily when you meet them inside Enum, ABC, and your ORM.

Tags :
Share :

Related Posts

Abstract Base Classes in Python with the abc Module

Abstract Base Classes in Python with the abc Module

Python leans on duck typing: if an object has the method you need, you call it and move on. That works well until you have a family of classes that a

Continue Reading
*args and **kwargs in Python: Flexible Function Signatures

*args and **kwargs in Python: Flexible Function Signatures

You've seen def wrapper(*args, **kwargs): in decorators, and probably super().__init__(**kwargs) in class hierarchies. These two parameters let a

Continue Reading
Asyncio in Python: A Beginner's Guide to Asynchronous Programming

Asyncio in Python: A Beginner's Guide to Asynchronous Programming

A lot of programs spend most of their time waiting. A web scraper waits for pages to download, an API server waits for the database, a chat bot waits

Continue Reading