What metaclasses are in Python
A metaclass is the class of a class. That sentence is correct and completely useless on its own, so this article builds up to it, shows when each hook actually fires, and then argues that you probably want something simpler.
Classes are objects
This is the piece you need first. In Python a class is not a compile-time declaration — it is an object, created at runtime, that you can pass around:
type(42) : int
type(Plain) : type <- the class itself has a type
type(type) : type <- type is its own type
isinstance(Plain, object) : True
42 is an instance of int. Plain is an instance of type. And type is
an instance of itself, which is where the recursion stops.
type() with three arguments builds a class
You have used type()
with one argument. With three it creates a class:
Dynamic = type("Dynamic", (), {"greet": lambda self: "hello", "version": 1})
type('Dynamic', (), {...}) -> <class '__main__.Dynamic'>
d.greet() : hello
d.version : 1
Name, base classes, namespace. This is not a trick — it is what the class
statement does. Python collects the body into a dictionary and calls type.
A metaclass replaces that call
If type is what normally builds your class, a metaclass is what you put in
its place:
class Meta(type):
def __new__(mcls, name, bases, namespace, **kwargs):
namespace["added_by_meta"] = True
return super().__new__(mcls, name, bases, namespace)
class Made(metaclass=Meta):
x = 1
Meta.__new__ running for 'Made'
bases : ()
namespace : ['method', 'x']
type(Made) : Meta
Made.added_by_meta : True <- the metaclass put it there
The metaclass saw the class being built, modified the namespace, and the attribute exists on the finished class.
When the hooks run
This is the part worth pinning down, because getting it wrong is the source of most metaclass confusion:
1. __prepare__ (returns the namespace mapping)
2. __new__ (creates the class object)
3. __init__ (class object already exists)
now instantiating:
4. __call__ (only when an INSTANCE is made)
The first three run once, when the class is defined. Not per instance.
Creating two instances of Made produces no further output at all:
creating two instances now -- note that Meta.__new__ does NOT run again:
(nothing printed)
__call__ is the odd one out — it runs when somebody writes Made(), and it
is how you control instantiation:
class SingletonMeta(type):
_instances = {}
def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
cls._instances[cls] = super().__call__(*args, **kwargs)
return cls._instances[cls]
Config() is Config() : True
Metaclasses are inherited
type(Child) : Meta <- inherited from Made
Subclasses get the metaclass too, and __new__ runs again for each of them —
with bases now non-empty. That difference is how the registry pattern
below avoids registering its own base class.
What they are actually used for
The honest answer is: a registry, an ORM, or a framework that needs to know about every subclass somebody writes.
class PluginMeta(type):
registry = {}
def __init__(cls, name, bases, namespace):
super().__init__(name, bases, namespace)
if bases: # skip the base class itself
PluginMeta.registry[name.lower()] = cls
registry after defining two subclasses : ['csvexporter', 'jsonexporter']
-> no explicit registration call anywhere
Defining a subclass is enough. Nobody has to remember a decorator or a registration call, which is exactly why Django models and SQLAlchemy work the way they do.
But you probably want init_subclass
Since Python 3.6 there is a hook that does the same job without a metaclass, from PEP 487:
class Base:
registry = {}
def __init_subclass__(cls, /, label=None, **kwargs):
super().__init_subclass__(**kwargs)
Base.registry[label or cls.__name__.lower()] = cls
class Alpha(Base, label="a"):
pass
registry : ['a', 'beta']
Same result, ordinary class, and it even takes keyword arguments from the class
definition. There is also
__set_name__
for the descriptor case.
Between them these two cover most of what metaclasses were reached for, and they avoid the following problem.
The reason to avoid them in library code
Every class has exactly one metaclass. If you inherit from two classes with different ones, Python cannot proceed:
class UsesA(metaclass=MetaA): pass
class UsesB(metaclass=MetaB): pass
class Both(UsesA, UsesB): pass
TypeError: metaclass conflict: the metaclass of a derived class must be a
(non-strict) subclass of the metaclasses of all its bases
The only fix is a third metaclass inheriting from both — which you can only write if you control both libraries. If you ship a metaclass in a public package, you have made your users’ inheritance decisions for them.
You already use them
Three you have certainly met:
type(abc.ABC) : ABCMeta
type(an Enum subclass) : EnumType
type(int) : type
abc uses ABCMeta to make
instantiating an abstract class an error, and
enum uses EnumType for the
member lookup and the iteration order. Both are good examples of the thing
being worth it: framework-level behaviour that would otherwise be manual on
every subclass.
About Netcup (advertisement)
The German host Netcup offers, among other things, affordable and powerful web hosting packages, KVM-based root servers and dedicated servers. With our voucher codes you can save even more (6€ off your first order, 30% off all KVM-based root servers, ...).