Metaclasses
Metaclasses are often oversold. The real skill is not "knowing metaclasses" but knowing where class decorators, descriptors, and `__init_subclass__` stop being enough.
Quick takeaway: use a metaclass only when you need to change class creation itself. For registration, validation, or light post-processing, simpler hooks usually work better.
Start With the Creation Timeline
Why It Matters
- Frameworks that feel "magical" usually hook into class creation.
- You need the right tool for the job: decorator, descriptor,
__init_subclass__, or metaclass. - Once you see the timeline, declarative ORM or validation APIs stop feeling mysterious.
Pick the Smallest Tool
| Tool | Usual purpose | Consider before a metaclass? |
|---|---|---|
| Class decorator | Post-process a class | Yes |
__init_subclass__ | Register or validate subclasses | Yes |
| Descriptor | Control field access and binding | Yes |
| Metaclass | Change how class objects are created | Last resort |
Example: Declarative Registration
py
class PluginRegistry(type):
registry: dict[str, type] = {}
def __new__(
mcls,
name: str,
bases: tuple[type, ...],
namespace: dict[str, object],
) -> type:
cls = super().__new__(mcls, name, bases, namespace)
plugin_name = namespace.get("plugin_name")
if isinstance(plugin_name, str):
mcls.registry[plugin_name] = cls
return cls
class PluginBase(metaclass=PluginRegistry):
plugin_name: str
class JsonPlugin(PluginBase):
plugin_name = "json"
class CsvPlugin(PluginBase):
plugin_name = "csv"
print(PluginRegistry.registry)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
This works well for declarative registration. But if registration is the only goal, `__init_subclass__()` is often simpler and easier for library users to compose.
When It Is Worth It
Good fit
You need to enforce class-creation policy or interpret class declarations as a DSL.
Bad fit
You only need light validation, registration, or attribute rewrites.
Tradeoff
Metaclasses affect inheritance and composition, so they raise the cost for downstream users.