Python Metaclasses Cheat Sheet
Covers how Python classes are created via type, writing custom metaclasses, and when to reach for simpler alternatives instead.
Classes Are Created by type
Every class is itself an instance of a metaclass.
class Foo: x = 1# Foo is itself an instance of typeprint(type(Foo)) # <class 'type'>print(isinstance(Foo, type)) # True# You can build an equivalent class dynamically with type()Foo2 = type("Foo2", (), {"x": 1})print(Foo2().x) # 1# type(name, bases, namespace) is exactly what the `class` statement calls
Writing a Metaclass
Hook into class creation itself, not just instance creation.
class Meta(type): def __new__(mcs, name, bases, namespace): # Runs before the class object is created for key, value in namespace.items(): if callable(value) and not key.startswith("__"): print(f"defining method: {key}") return super().__new__(mcs, name, bases, namespace) def __init__(cls, name, bases, namespace): # Runs after the class object is created super().__init__(name, bases, namespace)class MyClass(metaclass=Meta): def greet(self): return "hi"# Prints "defining method: greet" at class-definition time
Practical Use: Enforcing a Pattern
Metaclasses can enforce rules across all subclasses.
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]class Config(metaclass=SingletonMeta): def __init__(self): self.settings = {}a = Config()b = Config()assert a is b # Same instance every time
Key Concepts
Core vocabulary for metaclass programming.
- type- The default metaclass; type(obj) returns an object's class, type(cls) returns its metaclass
- metaclass=- Class keyword argument that specifies which metaclass builds the class
- __new__- On a metaclass, controls creation of the class object itself, not instances
- __init_subclass__- A simpler, often sufficient alternative to a metaclass for customizing subclasses
- __class_getitem__- Enables MyClass[int] generic subscript syntax without a metaclass
- ABCMeta- Standard-library metaclass (abc module) used to define abstract base classes
__set_name__ for Descriptor Wiring
A lighter-weight hook that fires when a descriptor is assigned as a class attribute, often replacing a metaclass.
class Field: def __set_name__(self, owner, name): # Called automatically at class-creation time, no metaclass needed self.name = f"_{name}" def __get__(self, instance, owner): if instance is None: return self return getattr(instance, self.name, None) def __set__(self, instance, value): setattr(instance, self.name, value)class Model: id = Field() name = Field()m = Model()m.name = "Ada"print(m.name, m._name) # Ada Ada
__prepare__: Controlling the Class Namespace
Override the mapping used to collect a class body before __new__ ever sees it.
from collections import OrderedDictclass OrderedMeta(type): @classmethod def __prepare__(mcs, name, bases, **kwargs): # Whatever is returned here becomes the namespace dict for the class body return OrderedDict() def __new__(mcs, name, bases, namespace, **kwargs): cls = super().__new__(mcs, name, bases, dict(namespace)) cls._field_order = list(namespace.keys()) return clsclass Record(metaclass=OrderedMeta): b = 1 a = 2print(Record._field_order) # ['__module__', '__qualname__', 'b', 'a'] in definition order
Auto-Registering Subclasses
A common real-world use: build a plugin/handler registry as classes are defined.
class PluginMeta(type): registry = {} def __new__(mcs, name, bases, namespace): cls = super().__new__(mcs, name, bases, namespace) if bases: # Skip registering the base class itself PluginMeta.registry[namespace.get("key", name.lower())] = cls return clsclass Plugin(metaclass=PluginMeta): key = Noneclass CsvPlugin(Plugin): key = "csv"class JsonPlugin(Plugin): key = "json"print(PluginMeta.registry) # {'csv': <class CsvPlugin>, 'json': <class JsonPlugin>}
Metaclass Conflicts & Resolution
When bases use different metaclasses, Python must derive a single consistent one.
class MetaA(type): passclass MetaB(type): passclass A(metaclass=MetaA): passclass B(metaclass=MetaB): passtry: class C(A, B): # TypeError: metaclass conflict passexcept TypeError as e: print(e)# Fix: define a metaclass that is a subclass of bothclass MetaC(MetaA, MetaB): passclass C(A, B, metaclass=MetaC): pass # Works: MetaC satisfies both requirements
Advanced Vocabulary
Terms that come up once you go past a first metaclass example.
- __mro_entries__- Lets a non-class object (e.g. Generic[T]) act as a base class by returning real bases to substitute
- __set_name__- Called on a descriptor right after the owning class is created, receiving the attribute name
- __instancecheck__/__subclasscheck__- Metaclass hooks that let isinstance()/issubclass() be customized (used by ABCMeta.register)
- six.with_metaclass / metaclass kwarg- Portable ways to declare a metaclass; Python 3 uses class C(Base, metaclass=Meta)
- type.__call__- The method actually invoked when you 'call' a class to instantiate it; override it to intercept construction
- abc.ABCMeta.register()- Registers a class as a 'virtual subclass' without it appearing in the MRO
- cls.__subclasses__()- Returns live direct subclasses; commonly combined with metaclass registries for plugin discovery
Reach for __init_subclass__ or a class decorator before writing a metaclass. As Tim Peters put it, 'metaclasses are deeper magic than 99% of users should ever worry about' — most registration or validation needs don't actually require one.