Skip to content
Search

Python:Metaclass

lastmod 2025-04-25created 2023-10-162 edits

In Python, classes are objects too. If classes are objects, does that mean there is another class that creates classes? There is!! This is called a "metaclass". Just as you create objects with a class, you can create classes with a metaclass.

Description

class MyMetaclass(type):
    def __new__(cls, name, bases, namespace):
        # Magic happens here
        return super().__new__(cls, name, bases, namespace)

class MyClass(metaclass=MyMetaclass):
    pass

obj = MyClass()

A Python class is not just a blueprint for objects. A class is itself an object! Every object needs a class that created it. So what creates class objects? Metaclasses.

By default, Python uses the type metaclass to create every class. For example, the following two classes are equivalent.

# Create a MyClass object
class MyClass:
    ...
obj = MyClass()

# Also creates a MyClass object
obj2 = type("MyClass", (), {})

To break down what those arguments mean, here is an example that creates a class with an attribute x and a method say_hi, that also subclasses off object.

# type(
#     name,
#     bases,
#     attributes
# )
CustomClass = type(
    'CustomClass',
    (object,),
    {'x': 5, 'say_hi': lambda self: 'Hello!'}
)

obj = CustomClass()
print(obj.x)  # 5
print(obj.say_hi())  # Hello!

In essence, a metaclass lets you customize and modify these arguments during class creation. For example, here is a metaclass that doubles every integer attribute of a class.

class DoubleAttrMeta(type):
    def __new__(cls, name, bases, namespace):
        new_namespace = {}
        for key, val in namespace.items():
            if isinstance(val, int):
                val *= 2
            new_namespace[key] = val
        return super().__new__(cls, name, bases, new_namespace)

class MyClass(metaclass=DoubleAttrMeta):
    x = 5
    y = 10

print(MyClass.x)  # 10
print(MyClass.y)  # 20

Here is another example of a metaclass that registers every created class in a registry.

# ===== Metaclass Solution =====
class RegisterMeta(type):
    registry = []
    def __new__(mcs, name, bases, attrs):
        cls = super().__new__(mcs, name, bases, attrs)
        mcs.registry.append(cls)
        return cls

The problem is that decorators can achieve the same goal without using black magic (and black magic is often cleaner, too).

# ===== Decorator Solution =====
def register(cls):
    registry.append(cls)
    return cls

@register
class MyClass:
    pass

And that exposes the biggest problem with metaclasses.

Almost 100% of the time, you will never need to touch them.

In everyday development, 99% of your code will never reach a use case where metaclasses are useful. And 95% of that 1% can be solved with plain decorators, dunder methods, or simple inheritance.

That is why there is a famous Python quote.

Metaclasses are deep magic that 99% of users should never need to worry about. If you wonder whether you need a metaclass, you don't. - Tim Peters

But if you are in the 1% with a unique problem that only metaclasses can solve, metaclasses are a powerful tool that lets you manipulate the internals of the Python object system.

Real-world examples of metaclasses:

  • Python's "ABC" implementation uses a metaclass to implement abstract classes.
  • Python's "Enum" implementation uses one to create enumeration types.
  • Many third-party libraries such as Django, SQLAlchemy, Pydantic, and Pytest use metaclasses for various purposes.

See also

Favorite site

References


  1. 14_Advanced_Python_Features_-_Edward_Li's_Blog.pdf ↩

author Zer0 sitemap