Skip to content

Metaclasses

In Python, the phrase “everything is an object” is taken literally. Integers are objects, strings are objects, and even classes themselves are objects.

If a class is an object, it must be an instance of something. That “something” is a Metaclass. Understanding metaclasses allows you to write code that creates, modifies, or validates classes automatically as they are being defined.


To understand metaclasses, we must follow the chain of creation:

  1. The Instance: Created by a Class. (e.g., my_obj = MyClass())
  2. The Class: Created by a Metaclass. (e.g., class MyClass: ...)
  3. The Metaclass: The factory that builds classes.

By default, the metaclass for all classes in Python is the built-in type.

class Robot:
pass
r = Robot()
print(type(r)) # <class '__main__.Robot'> (The class of the instance)
print(type(Robot)) # <class 'type'> (The class of the class)
print(type(type)) # <class 'type'> (Type is its own metaclass)

Definition: A Metaclass is simply a class whose instances are other classes.


Most developers use the class keyword, but you can actually create a class manually using type(). This reveals the three ingredients required to build a class:

Syntax: type(name, bases, dict)

  • name: String; the name of the class.
  • bases: Tuple; the parent classes (inheritance).
  • dict: Dictionary; the attributes and methods of the class.
dynamic_class.py
def greet(self):
print(f"Hello, I am {self.name}")
# Create a class named 'User' on the fly
User = type("User", (object,), {"name": "Anonymous", "say_hi": greet})
u = User()
u.say_hi() # Output: Hello, I am Anonymous

To create your own metaclass, you inherit from type. You then override the __new__ method, which is responsible for creating the class object itself.

When Python encounters a class block, it gathers the name, bases, and attributes into a dictionary. It then looks for a metaclass=. If found, it calls the metaclass to “build” the class.

simple_metaclass.py
class VerifyMeta(type):
def __new__(cls, name, bases, dct):
print(f"[*] Intercepting creation of: {name}")
# Logic: Ensure all classes have a 'description' attribute
if "description" not in dct:
raise TypeError(f"Class '{name}' must define a 'description' attribute")
return super().__new__(cls, name, bases, dct)
# This will work
class Plugin(metaclass=VerifyMeta):
description = "A basic plugin"
# This will raise a TypeError immediately upon definition
# class BadPlugin(metaclass=VerifyMeta):
# pass

It is common to confuse these two when writing metaclasses:

  • __new__: Called before the class is created. Use this if you want to modify the class attributes or bases before the class exists.
  • __init__: Called after the class has been created. Use this for configuration that doesn’t require changing the fundamental structure of the class.

5. Real-World Context: Why use Metaclasses?

Section titled “5. Real-World Context: Why use Metaclasses?”

Metaclasses are a “heavy duty” tool. You should rarely use them in application code, but they are essential for library and framework authors.

Use Case: Automatic Attribute Registration

Section titled “Use Case: Automatic Attribute Registration”

Imagine a framework where every “Service” class needs to be registered in a central registry.

registry_example.py
REGISTRY = {}
class ServiceMeta(type):
def __new__(cls, name, bases, dct):
new_class = super().__new__(cls, name, bases, dct)
if name != "BaseService":
REGISTRY[name.lower()] = new_class
return new_class
class BaseService(metaclass=ServiceMeta):
pass
class EmailService(BaseService):
pass
class DatabaseService(BaseService):
pass
print(REGISTRY.keys()) # dict_keys(['emailservice', 'databaseservice'])

There is an even deeper hook called __prepare__. This method is called before the class body is even executed. It returns the dictionary (namespace) that will be used to store the class attributes.

By default, this is a standard dict, but you could return an OrderedDict (if you were on an old version of Python) to preserve the order in which methods were defined.


As Tim Peters (author of the Zen of Python) famously said: “Metaclasses are deeper magic than 99% of users should ever worry about. If you wonder whether you need them, you don’t.”

Alternatives to Metaclasses:

  1. Class Decorators: Simpler and often sufficient for adding methods or modifying attributes.
  2. __init_subclass__: Introduced in Python 3.6, this allows parent classes to customize child classes without needing a full metaclass.