Metaclasses in Python are special classes that create and manage other classes. Since everything in Python is an object—including classes—metaclasses can be thought of as "classes for classes." They provide a mechanism to control the class creation process and modify class behavior at definition time.
How Metaclasses Work
When Python encounters a class definition, it automatically uses the type metaclass to create that class. Here's what happens under the hood:
class MyClass:
pass
Internally, Python executes:
MyClass = type('MyClass', (), {})
Where:
'MyClass'— the class name()— a tuple of parent classes{}— a dictionary of class attributes and methods
The built-in type metaclass is responsible for creating all classes in Python by default.
Creating Custom Metaclasses
Custom metaclasses are created by inheriting from type and overriding its methods:
class MyMeta(type):
def __new__(cls, name, bases, attrs):
print(f"Creating class {name}")
attrs['created_by_metaclass'] = True
return super().__new__(cls, name, bases, attrs)
class MyClass(metaclass=MyMeta):
pass
print(MyClass.created_by_metaclass) # True
Key Metaclass Methods
__new__
Controls class creation. Called before __init__ and returns the new class:
class CreationMeta(type):
def __new__(cls, name, bases, attrs):
# Modify attributes before the class is created
attrs['class_id'] = id(cls)
return super().__new__(cls, name, bases, attrs)
__init__
Initializes the newly created class:
class InitMeta(type):
def __init__(cls, name, bases, attrs):
print(f"Initializing class {name}")
super().__init__(name, bases, attrs)
__call__
Defines behavior when class instances are created:
class CallMeta(type):
def __call__(cls, *args, **kwargs):
print(f"Creating instance of {cls.__name__}")
return super().__call__(*args, **kwargs)
Practical Use Cases
Automatic Attribute Transformation
class UpperAttrMeta(type):
def __new__(cls, name, bases, attrs):
uppercase_attrs = {}
for attr_name, attr_value in attrs.items():
if not attr_name.startswith('__'):
uppercase_attrs[attr_name.upper()] = attr_value
else:
uppercase_attrs[attr_name] = attr_value
return super().__new__(cls, name, bases, uppercase_attrs)
class MyClass(metaclass=UpperAttrMeta):
attr1 = 'value1'
attr2 = 'value2'
print(hasattr(MyClass, 'ATTR1')) # True
print(hasattr(MyClass, 'attr1')) # False
Singleton Pattern Implementation
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 DatabaseConnection(metaclass=SingletonMeta):
def __init__(self):
print("Creating database connection")
db1 = DatabaseConnection()
db2 = DatabaseConnection()
print(db1 is db2) # True
Class Structure Validation
class ValidatedMeta(type):
def __new__(cls, na