Course Progress78%
🍎 Python Advanced Python Topic 78 / 100
⏳ 8 min read

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."

— ShurAI

Classes 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:

python
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.
Three levels of Python’s object model:
type  (metaclass — creates classes)
↓ instance of type
Dog  (class — creates objects)
↓ instance of Dog
rex  (object)

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:

python
# 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:

python — auto-uppercase method names
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

python
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'>}
Do you need metaclasses?

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?