Metaclasses
If a class is a blueprint for objects, a metaclass is a blueprint for classes. This is as deep as Python goes — but understanding it unlocks how frameworks like Django and SQLAlchemy work their magic.
Everything in Python is an Object
Classes themselves are objects — instances of type. type is the default metaclass. When Python sees class Foo:, it calls type("Foo", bases, namespace) to create it.
class Dog:
species = "Canis familiaris"
def bark(self): return "Woof!"
# Dog itself is an object
print(type(Dog)) # <class 'type'>
print(type(42)) # <class 'int'>
print(type("hi")) # <class 'str'>
# type() can CREATE classes dynamically
Cat = type("Cat", (), {
"sound": "Meow",
"speak": lambda self: self.sound
})
cat = Cat()
print(type(Cat)) # <class 'type'>
print(cat.speak()) # Meow
# Check the MRO (method resolution order)
class A: pass
class B(A): pass
class C(B): pass
print(C.__mro__) # (C, B, A, object)
Output
Creating a Metaclass
Subclass type, override __new__ or __init__, and use it via metaclass=. This lets you intercept and modify class creation.
class UpperAttrMeta(type):
"""Metaclass that converts all attribute names to UPPERCASE."""
def __new__(mcs, name, bases, namespace):
upper_ns = {
k.upper() if not k.startswith("_") else k: v
for k, v in namespace.items()
}
return super().__new__(mcs, name, bases, upper_ns)
class MyClass(metaclass=UpperAttrMeta):
greeting = "hello"
value = 42
def say(self):
return "speaking"
obj = MyClass()
print(obj.GREETING) # hello (attribute was uppercased!)
print(obj.VALUE) # 42
print(obj.SAY()) # speaking
# This is how ORM libraries like SQLAlchemy work:
# you write normal class attributes, the metaclass
# transforms them into database column definitions
Output
Practical: Singleton Pattern via Metaclass
class SingletonMeta(type):
"""Ensures only one instance of a class ever exists."""
_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, url):
self.url = url
print(f"Connecting to {url}...")
def query(self, sql):
return f"Results for: {sql}"
# First call creates the instance
db1 = DatabaseConnection("postgresql://localhost/mydb")
db2 = DatabaseConnection("postgresql://localhost/other") # reuses existing!
print(db1 is db2) # True — same object
print(db1.url) # postgresql://localhost/mydb
print(db2.url) # same — no second connection made
Output
When should you use metaclasses?Almost never in application code. But knowing how they work explains Flask's
@app.route, Django's Model, SQLAlchemy's column definitions — all use metaclasses (or similar __init_subclass__ hooks) under the hood.