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
- Understanding Python metaclasses easily and deeply, Python Metaclass A to Z
- [Recommended] Advanced Python features | GeekNews
- [Original] 14 Advanced Python Features | Edward Li's Blog 1
References
-
14_Advanced_Python_Features_-_Edward_Li's_Blog.pdf ↩