Metaclasses
The class of a class — how Python creates classes, and how to customise that process with a custom metaclass.
"If a class is a blueprint for objects, a metaclass is a blueprint for classes. Most Python programmers never need to write one — but understanding them demystifies how Python works."
— ShurAIClasses are Objects Too
In Python, everything is an object — including classes themselves. When you write class Dog:, Python creates a class object. The type of a class object is its metaclass:
class Dog:
pass
print(type(42)) # <class 'int'> — 42 is an instance of int
print(type("hello")) # <class 'str'>
print(type(Dog)) # <class 'type'> — Dog is an instance of type!
# 'type' is Python's built-in metaclass.
# Every class you write is an instance of type by default.
Creating a Class with type() Directly
type() can be called with three arguments to create a class programmatically — this is exactly what Python does behind the scenes when it processes a class statement:
# These two are equivalent:
# 1. Normal class syntax
class Dog:
def bark(self):
print("Woof!")
# 2. type() directly: type(name, bases, dict)
Dog = type("Dog", (), {"bark": lambda self: print("Woof!")})
Dog().bark() # Woof!
Writing a Custom Metaclass
A metaclass lets you intercept and customise class creation. Common uses: auto-registering classes, enforcing conventions, or adding methods automatically:
class UpperMeta(type):
"""Metaclass that uppercases all method names."""
def __new__(mcs, name, bases, namespace):
new_namespace = {}
for key, value in namespace.items():
if key.startswith("__"): # keep dunders unchanged
new_namespace[key] = value
else:
new_namespace[key.upper()] = value # SHOUT
return super().__new__(mcs, name, bases, new_namespace)
class Shout(metaclass=UpperMeta):
def hello(self):
print("Hello!")
Shout().HELLO() # Hello! — method was renamed to HELLO
Real Use: Class Registry
class PluginMeta(type):
"""Auto-register every subclass into a registry dict."""
registry = {}
def __init__(cls, name, bases, namespace):
super().__init__(name, bases, namespace)
if bases: # skip the base class itself
PluginMeta.registry[name] = cls
class Plugin(metaclass=PluginMeta):
pass
class AudioPlugin(Plugin): pass # auto-registered
class VideoPlugin(Plugin): pass # auto-registered
print(PluginMeta.registry)
# {'AudioPlugin': <class 'AudioPlugin'>, 'VideoPlugin': <class 'VideoPlugin'>}
Rarely. Most use cases for metaclasses can be solved more simply with class decorators, __init_subclass__, or @dataclass. Metaclasses are for framework authors who need to fundamentally change how a whole family of classes behaves. Understanding them deepens your Python knowledge even if you never write one.
"Metaclasses are deeper magic than 99% of users will ever need. If you're wondering whether you need a metaclass, you probably don't. — Tim Peters"
— ShurAI🧠 Quiz — Q1
What is the default metaclass for every Python class?
🧠 Quiz — Q2
What does type("Dog", (), {"bark": ...}) do?
🧠 Quiz — Q3
A custom metaclass inherits from what?
🧠 Quiz — Q4
Which is the BEST reason to write a custom metaclass?