Command Palette

Search for a command to run...

Hectal
PHASE 13Advanced ~26 min· topic 6 of 9

Topic 13.6

Metaclasses and Class Creation

In one line

A class is an object made by another object, type. Knowing how class statements call type(name, bases, namespace) explains metaclasses, but most needs are met more simply by __init_subclass__ and class decorators.

Think of it like this

A cookie cutter and a cookie. The cookie cutter (the class) stamps out cookies (the instances). But someone also had to make the cutter. A machine that makes cookie cutters is a metaclass. In Python the machine that makes classes is called type, and you can swap it for your own to change how classes get built.

Words you'll meet

New words in this topic, in plain English. Come back here whenever one feels fuzzy.

Metaclass
The class of a class. A class that makes other classes. By default it is type.
`type(name, bases, namespace)`
Builds a new class from a name, a tuple of parent classes and a dict of attributes.
Namespace
The dictionary of names defined in a class body.
`__new__`
The method that creates the object (instance or class) before it is initialised.
`__init_subclass__`
A hook called on a base class whenever a subclass is defined.
Class decorator
A function applied to a finished class that returns the same or a changed class.
Registry
A dictionary that collects classes or functions so they can be looked up by name.
Plugin
A class that adds a feature and registers itself so the main program can find it.

Step by step

01A class is an object made by type

type(Dog) is type. And Dog = type("Dog", (), {...}) builds the same kind of class a class statement would. The dictionary holds the methods and attributes; here a lambda (a one-line unnamed function, Topic 5.6) is the speak method.

main.pywhole filepython
Dog = type("Dog", (), {"sound": "Woof", "speak": lambda self: self.sound})
print(Dog().speak(), Dog.__name__)
print(type(Dog).__name__)

02What a class statement does

Python runs the class body as a function, collecting the names into a namespace dictionary. Then it finds the metaclass (the metaclass= keyword if given, otherwise the most derived metaclass among the types of the bases, otherwise type; if two bases have unrelated metaclasses this is where the conflict error comes from) and calls it with the name, the bases and the namespace.

What a class statement doesdiagram
Rendering diagram…

03Watch the order

The first runnable example prints at each step. You see the metaclass hooks run when the class statement finishes, before any instance exists. Then Base() goes through Meta.__call__, Base.__new__, Base.__init__.

04The simpler tool: __init_subclass__

Plugin.__init_subclass__ runs when class CsvReader(Plugin) is defined, so it can add the new class to a registry. Keyword arguments in the class line, such as name="json", arrive as parameters. (The / in the signature means cls is positional-only, Topic 5.4.) Always call super().__init_subclass__(**kwargs) so cooperating bases work.

main.pywhole filepython
class Plugin:
    registry = {}

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

class CsvReader(Plugin):
    pass

class JsonReader(Plugin, name="json"):
    pass

print(sorted(Plugin.registry))

05Class decorators

A class decorator is just a function called with the finished class. @add_repr here reads the class attributes and attaches a __repr__. You will recognise the shape from function decorators (Topic 9.6) and from @dataclass (Topic 8.8).

06Where frameworks use this

Django and SQLAlchemy models collect the fields you declare in the class body when the class is created. Enum turns your list of names into members. ABCMeta records which abstract methods are missing, so Shape() can refuse to be created. Pydantic and dataclass-like libraries read the class annotations at creation time. A plugin registry for model back-ends or tools in an agent framework is usually __init_subclass__.

07A decision guide

Need to change a class after it is made, or add a method? Use a class decorator. Need to react to subclasses (register, validate)? Use __init_subclass__. Need to change the namespace before the class exists, intercept instance creation, or customise how the class itself behaves with operators and built-ins (len(MyClass), for member in MyClass, MyClass["name"] as Enum does)? Then a metaclass is the right tool, used sparingly. (For generic-style MyClass[int] you do not need one: define __class_getitem__ on the class.)

Try it yourself

  1. 1

    Create a class at run time

    Use type to build a class Cat with a sound attribute and a speak method, create an instance and call it. Then print type(Cat) and Cat.__mro__.

  2. 2

    Register with a different key

    In the second example add class XmlReader(Plugin, name="xml") and print the sorted keys. Then define a class that forgets super().__init_subclass__(**kwargs) in a second base class and see which registry misses it.

  3. 3

    Reject a bad subclass

    Make __init_subclass__ raise TypeError when a subclass has no sides attribute defined in its own body. The error appears when the class is defined, not when it is used, which is exactly when you want it.

Code & diagrams

Metaclass hooks and type() by hand New tab
Sign in to run this example in your browser.

Expected output

Meta.__new__ builds the class Base
Meta.__init__ finishes the class Base
-- class statement finished --
Meta.__call__ makes an instance of Base
Base.__new__ allocates the instance
Base.__init__ fills it in
Meta Meta type
Woof Dog
A plugin registry with __init_subclass__, and a class decorator New tab
Sign in to run this example in your browser.

Expected output

['csvreader', 'json']
JsonReader
Point(x=1, y=2)
Instance creation and class creation side by sidediagram
Rendering diagram…

Break it on purpose

Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.

Break #1

Combine two metaclasses

Inherit from two classes that use different metaclasses.

main.pywhole filepython
class Meta1(type):
    pass


class Meta2(type):
    pass


class A(metaclass=Meta1):
    pass


class B(metaclass=Meta2):
    pass


class C(A, B):
    pass
terminal
$ python main.py
── what you'll see ──
Traceback (most recent call last):
File "main.py", line 17, in <module>
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

Myth vs fact

Myth

You need a metaclass to customise class creation.

Fact

__init_subclass__ and class decorators cover most needs. A metaclass is the last resort.

Myth

class is special syntax unrelated to type.

Fact

A class statement is a call to the metaclass (usually type) with the name, bases and the namespace built from the body.

Myth

A metaclass makes instances.

Fact

A metaclass makes classes. The class makes instances. A metaclass __call__ only intercepts the step that makes instances.

Myth

Metaclasses are dead since __init_subclass__ exists.

Fact

They still power ABCMeta, EnumMeta and ORMs, and they are the right tool for things like class-level len() and iteration (as in Enum) or a custom __prepare__ namespace.

Pro corner

Extra depth for experienced readers. New to this? Skip it for now and come back later.

  • ▸

    The class body is executed with the namespace returned by Meta.__prepare__, which can be an ordered or special dict. That is how Enum rejects duplicate names and how some DSLs (small mini-languages built inside Python code) record the order of declared fields. Since 3.6 the default namespace keeps definition order.

  • ▸

    Order at class creation, roughly: body runs, Meta.__new__, then __set_name__ on each descriptor in the namespace, then the base class's __init_subclass__, then Meta.__init__. If your hooks depend on each other, test the order for your Python version rather than memorising it.

  • ▸

    typing.Protocol, dataclass and Pydantic models all inspect __annotations__ at class creation time. Deferred annotations in 3.14 change when annotations are evaluated, which matters to code that reads them from a metaclass.

  • ▸

    __init_subclass__ runs for each subclass, never for the class that defines it, and it runs after the class object exists, so it cannot change the namespace the way Meta.__new__ can; it can still add attributes with setattr. __set_name__ and __init_subclass__ together replaced most 2.x-era metaclass uses (six.with_metaclass, ORMs naming their fields). types.new_class and type(...) build classes at run time, and type is its own metaclass (type(type) is type), which is where the chain of 'what made this?' ends.

  • ▸

    Metaclass cost and traps: a metaclass __call__ runs on every instantiation, so keep it tiny; a metaclass __getattr__ or __setattr__ affects class attribute access (Cls.x), not instance access; and isinstance and issubclass can be customised on a metaclass through __instancecheck__ and __subclasscheck__, which is how ABCMeta lets unrelated classes count as virtual subclasses.

  • ▸

    Registering subclasses is a common pattern in AI tooling: model back-ends, tools for an agent, loaders and evaluators are found by name through a registry like the example. The danger is an import-time side effect: a plugin registers only if its module is imported, so import them explicitly.

Remember this

  1. 1

    In Python, a class is an object, and like every object it has a type. The type of an ordinary class is type. type(5) is int, and type(int) is type. So type is a class factory: type(name, bases, namespace) creates a new class directly. A class Dog: ... statement is a convenient way to run the body into a dictionary (the namespace) and then call type("Dog", (), namespace).

  2. 2

    Creating an instance and creating a class both follow two steps. For instances: __new__ allocates the object, then __init__ fills it in. For classes: the metaclass's __new__ builds the class object, then its __init__ finishes it. Writing Cat() calls type(Cat).__call__, which runs Cat.__new__ and Cat.__init__; a metaclass can customise that too through __call__.

  3. 3

    A metaclass is a class that inherits from type, used by writing class Base(metaclass=Meta). Its __new__(mcls, name, bases, namespace) can read and change the namespace before the class exists (for example, add methods or check for required names). Its __init__ runs after the class exists. Subclasses of Base use the same metaclass automatically, since the class's type is inherited.

  4. 4

    Metaclasses are powerful and rarely needed. Tim Peters' famous line is that metaclasses are deeper magic than 99 percent of users should worry about. They also conflict: a class cannot have two unrelated metaclasses, which causes TypeError: metaclass conflict when you combine bases from two frameworks. Before reaching for one, use a simpler tool.

  5. 5

    **__init_subclass__** (Python 3.6) is that simpler tool. It is a classmethod on the base class that Python calls every time someone defines a subclass, with the new subclass as cls and any class keyword arguments (class JsonReader(Plugin, name="json")). With it you can register subclasses in a registry or validate them, with no metaclass and no conflict risk.

  6. 6

    A class decorator (@add_repr above a class) is another simple tool. It receives the finished class, can change it or wrap it, and returns it. Dataclasses are a class decorator. Choose in this order: class decorator or __init_subclass__ first, a metaclass only for what they cannot do (changing the namespace before the class exists, a custom __call__, or __prepare__). Real metaclass users are abc.ABCMeta, enum.EnumMeta, and ORMs such as Django and SQLAlchemy.

Explain it without notes

01

What is a metaclass, and what is the default one?

02

Explain the order of __new__, __init__ and __call__ when you create a class and when you create an instance.

03

When would you use __init_subclass__ instead of a metaclass?

04

What is a class decorator, and how does it differ from a metaclass?

Practice

01

Use type() to create a class Cat with a sound attribute and a speak method, then call it.

02

Write a base class whose __init_subclass__ raises TypeError unless the subclass defines sides in its own body.

03

Write a class decorator register that appends each decorated class name to a list.

Trade-offs

  • ↔

    Power versus readability. Metaclasses can do anything at class creation, and nobody reading the code can see what happens without finding the metaclass. Prefer the least powerful tool that works.

  • ↔

    Import-time work. Code that runs when a class is defined (registries, validation) runs at import. It fails early, which is good, but it slows start-up and hides dependencies on import order.

Done when you can

  • Done when you can explain that a class is an object created by type.

  • Done when you can build a class with type(name, bases, namespace).

  • Done when you can write an __init_subclass__ registry and a class decorator.

  • Done when you can say in what order class-creation hooks run.

  • Done when you can explain a metaclass conflict and how to avoid it.