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