Skip to main content

Metaclasses: Creating Classes that Create Classes in Python

A metaclass is a class whose instances are classes. Just as a normal class defines the structure of instances (objects), a metaclass defines the structure of classes. In Python, type is the default metaclass for all classes—type is the parent of type itself. By subclassing type, you create a custom metaclass that intercepts and customizes class creation, allowing you to modify class behavior, enforce invariants, or generate methods dynamically at class definition time. Metaclasses are powerful but notoriously complex; they're used in production frameworks (Django models, SQLAlchemy declarative base, Celery tasks) to provide transparent, intuitive APIs. Understanding metaclasses completes your mastery of Python's object model.

Understanding type: The Default Metaclass​

Every class in Python is an instance of a metaclass. By default, all classes are instances of type:

class Dog:
pass

print(type(Dog)) # Output: <class 'type'>
print(isinstance(Dog, type)) # Output: True
print(type(Dog) is type) # Output: True

# Classes are instances of type, just as instances are instances of classes
dog = Dog()
print(type(dog)) # Output: <class '__main__.Dog'>
print(isinstance(dog, Dog)) # Output: True

type is both a class and its own metaclass. type(Dog) returns type, and type(type) returns type. This recursive self-reference is unique to type and is the foundation of Python's object model.

Creating Classes Dynamically with type​

You can create a class at runtime by calling type with three arguments: type(name, bases, attrs):

# Create a class dynamically
Dog = type('Dog', (), {
'species': 'Canis familiaris',
'bark': lambda self: 'Woof!'
})

dog = Dog()
print(dog.species) # Output: Canis familiaris
print(dog.bark()) # Output: Woof!

# Create a class with a parent
class Animal:
def move(self):
return "Moving..."

Cat = type('Cat', (Animal,), {
'meow': lambda self: 'Meow!'
})

cat = Cat()
print(cat.move()) # Output: Moving...
print(cat.meow()) # Output: Meow!

This dynamic class creation is how frameworks generate classes at runtime. Understanding this syntax is the foundation for understanding metaclasses.

Creating a Custom Metaclass​

A custom metaclass is a class that inherits from type. It overrides the __new__ and/or __init__ methods to customize how classes are created:

class SingletonMeta(type):
"""A metaclass that enforces the singleton pattern."""
_instances = {}

def __call__(cls, *args, **kwargs):
"""Called when a class is instantiated."""
if cls not in SingletonMeta._instances:
instance = super().__call__(*args, **kwargs)
SingletonMeta._instances[cls] = instance
return SingletonMeta._instances[cls]


class DatabaseConnection(metaclass=SingletonMeta):
"""Only one instance of this class can exist."""

def __init__(self, host='localhost'):
self.host = host
print(f'Connecting to {host}...')


# First instantiation
conn1 = DatabaseConnection('db.example.com')

# Second instantiation returns the same instance
conn2 = DatabaseConnection('different-host')

print(conn1 is conn2) # Output: True
print(conn1.host) # Output: db.example.com (unchanged)

The metaclass's __call__ method is invoked when you instantiate a class. By overriding it, you intercept instance creation and can implement patterns like singletons, instance pooling, or lazy initialization.

Metaclass __new__ for Class Transformation​

Override __new__ to customize the class object itself before it's fully created. This is where you add methods, modify attributes, or enforce naming conventions:

class ValidatedMeta(type):
"""A metaclass that validates class definitions."""

def __new__(mcs, name, bases, namespace):
# Enforce that public methods are documented
for attr_name, attr_value in namespace.items():
if callable(attr_value) and not attr_name.startswith('_'):
if not attr_value.__doc__:
raise TypeError(
f'Method {name}.{attr_name} must have a docstring'
)

# Create the class
cls = super().__new__(mcs, name, bases, namespace)

# Add a classmethod that lists all public methods
@classmethod
def list_public_methods(cls):
return [
name for name in dir(cls)
if not name.startswith('_') and callable(getattr(cls, name))
]

cls.list_public_methods = list_public_methods

return cls


class Calculator(metaclass=ValidatedMeta):
def add(self, a, b):
"""Add two numbers."""
return a + b

def subtract(self, a, b):
"""Subtract b from a."""
return a - b


calc = Calculator()
print(calc.add(3, 2)) # Output: 5
print(Calculator.list_public_methods()) # Output: ['add', 'subtract', 'list_public_methods']

# This raises TypeError because multiply has no docstring
try:
class BadCalculator(metaclass=ValidatedMeta):
def multiply(self, a, b):
return a * b
except TypeError as e:
print(f'Error: {e}')

Metaclass __new__ runs before the class is fully defined, allowing you to validate, transform, or enhance the class definition.

Metaclass Inheritance and MRO​

When you define a class hierarchy with metaclasses, the metaclass is inherited:

class DebugMeta(type):
"""A metaclass that logs class creation."""
def __new__(mcs, name, bases, namespace):
print(f'Creating class {name}')
return super().__new__(mcs, name, bases, namespace)


class Parent(metaclass=DebugMeta):
pass
# Output: Creating class Parent


class Child(Parent):
pass
# Output: Creating class Child

Child inherits DebugMeta from Parent, so creating Child triggers DebugMeta.__new__. This is useful for enforcing patterns across class hierarchies.

Comparison: Metaclasses vs. Class Decorators vs. __init_subclass__​

MechanismWhenControlComplexity
MetaclassClass creation (before definition completes)Full; can prevent creationHigh
Class decoratorClass definition time (after creation)Full; clean syntaxLow
__init_subclass__Subclass definition timeOn subclasses only; automaticLow

Key Takeaways​

  • A metaclass is a class whose instances are classes; the default metaclass is type.
  • Create a custom metaclass by subclassing type and overriding __new__ and/or __init__.
  • Metaclasses allow you to customize how classes are created, enforce naming conventions, and add methods at class definition time.
  • For most tasks, __init_subclass__ or class decorators are simpler and preferred; reserve metaclasses for complex framework-level control.

Frequently Asked Questions​

What is the difference between metaclasses and class decorators?​

Both customize classes, but metaclasses control class creation itself (can prevent creation, modify bases), while decorators are applied after creation. Decorators are usually simpler; use metaclasses only when you need pre-creation control.

Can I use multiple metaclasses on one class?​

Yes, if the metaclasses share a common base. If they don't, Python raises a TypeError. You can resolve this by creating a metaclass that inherits from all conflicting metaclasses.

Why is type(type) equal to type?​

Because type is its own metaclass. type is both a class and the metaclass for all classes (including itself). This is a special case in Python's object model.

Do metaclasses slow down class creation?​

Yes, slightly. Creating a class with a custom metaclass is slower than using type directly because your metaclass code runs. However, class creation happens once at import time, so the performance impact is negligible.

Can I override __call__ on a metaclass?​

Yes. __call__ on a metaclass is invoked when you instantiate a class (create an instance). This allows you to customize instantiation without subclassing the class itself—ideal for singletons or instance pooling.

Further Reading​