Skip to main content
A metaclass in Python is a class whose instances are classes. While a standard class defines the behavior and memory layout of its objects, a metaclass defines the behavior, structure, and instantiation process of the class itself. In Python’s object model, everything is an object, including classes; therefore, every class must be instantiated by a metaclass. The default metaclass for all new-style classes is the built-in type.

The type Metaclass

When the Python interpreter executes a class statement, it translates the class definition into a call to the type metaclass. The type constructor dynamically creates the class object using three arguments:
  • name: The string name of the class (becomes the __name__ attribute).
  • bases: A tuple of base classes for inheritance (becomes the __bases__ attribute).
  • namespace: A dictionary containing the class body, including methods and attributes. This dictionary is used to populate the class’s namespace, but it is copied into a read-only mappingproxy object, which is what actually becomes the class’s __dict__ attribute. Modifying the original dictionary after class creation does not affect the class.

Defining a Custom Metaclass

To create a custom metaclass, you inherit from type and override its dunder (magic) methods to intercept the class creation pipeline. The four primary methods involved in the metaclass lifecycle are:

1. __prepare__

Called before the class body is executed. It returns a dictionary-like object that acts as the local execution namespace for the class body. Any keyword arguments passed in the class definition are received here. Because the built-in type.__prepare__ does not accept arbitrary keyword arguments, any custom keyword arguments must be stripped before calling super().__prepare__.

2. __new__

Called to allocate memory and create the actual class object. This is the most frequently overridden method in a metaclass, as it allows modification of the class namespace, name, or bases before the class object is compiled into memory. Like __prepare__, type.__new__ rejects arbitrary keyword arguments. Any custom keyword arguments passed to the class definition must be stripped before calling super().__new__.

3. __init__

Called after the class object is created by __new__. It is used to initialize the class object, similar to how a standard __init__ initializes an instance. At this stage, the class object already exists. Similar to __new__, type.__init__ does not accept arbitrary keyword arguments, so they must be omitted when calling super().__init__.

4. __call__

Called when the newly created class is instantiated (e.g., obj = MyClass()). Overriding this in a metaclass intercepts the creation of the class’s instances, controlling exactly how and when the class’s own __new__ and __init__ methods are invoked.

Applying a Metaclass

To assign a metaclass to a class, pass the metaclass keyword argument in the class definition signature. Any additional keyword arguments provided here will be passed to the metaclass’s __prepare__, __new__, and __init__ methods.

Metaclass Resolution Order

When a class inherits from multiple base classes, Python must determine the correct metaclass to use for the derived class. Python enforces a strict rule: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases. If base classes possess different metaclasses that do not share a subclass relationship, the interpreter cannot resolve a single most-derived metaclass and will raise a TypeError (commonly known as a metaclass conflict).
Tired of Poor Python Skills? Fix That With Deep Grasping!Learn More