Python Metaclasses Tutorial: type(), __new__, __call__ and Real Examples

A logistics client wanted every shipping-rate class to register itself and refuse to load without a price per pound. A metaclass does exactly that: it’s the class of a class, and its code runs the moment Python executes a class statement.

A missing rate used to blow up only at checkout, hours after a deploy. Toy examples with type(name, bases, dict) don’t fix that.

Quick answer: subclass type, override __new__ to inspect or change the class body, and attach it with metaclass=. Python then calls your code once per class definition:

class CarrierMeta(type):
    def __new__(mcs, name, bases, namespace):
        cls = super().__new__(mcs, name, bases, namespace)
        print("created", name)
        return cls

class Carrier(metaclass=CarrierMeta):
    pass

In this tutorial, I’ll explain python metaclasses with one running sample, a set of carrier classes, and cover type(), __new__, __init__, __call__, __prepare__, the simpler __init_subclass__ and the errors you’ll actually hit.

What Is a Metaclass in Python?

Every value in Python is an object, and objects come from classes. Classes are objects too, so something has to create them. That something is a metaclass, and the default one is type (one reason Python counts as an object-oriented language all the way down).

class Shipment:
    pass

order = Shipment()
print(type(order))
print(type(Shipment))
print(type(type))
print(isinstance(Shipment, type), isinstance(order, Shipment))

Output:

<class '__main__.Shipment'>
<class 'type'>
<class 'type'>
True True

I ran every example here with Python 3.14.8. order is an instance of Shipment, and Shipment is an instance of type. Even type is its own type, which is where the chain stops.

How to Create a Class Dynamically with type()

Called with one argument, type() tells you an object’s class. Called with three, it builds a new class: a name, a tuple of base classes and a dict that becomes the class body.

def describe(self):
    return f"{self.name}: ${self.rate_per_lb:.2f} per lb"

Ground = type("Ground", (), {"name": "Ground", "rate_per_lb": 0.85,
                             "describe": describe})
Express = type("Express", (Ground,), {"name": "Express",
                                      "rate_per_lb": 2.40})

print(Ground().describe())
print(Express().describe())
print(Express.__bases__, type(Express))

Output:

Ground: $0.85 per lb
Express: $2.40 per lb
(<class '__main__.Ground'>,) <class 'type'>

Both classes behave like ones written with class. Express inherits describe() from Ground, and its type is still plain type. If you only need to add attributes to an existing class, setattr() and getattr() are simpler.

That third argument is the same mapping you later see as the class’s __dict__ attribute. A metaclass gets this dict before the class exists, which is what makes it powerful.

Note: type(obj) ignores inheritance. Use isinstance() for checks, and keep type() for building classes or exact-type debugging.

How to Write a Custom Metaclass with __new__

Here’s the client’s version. CarrierMeta.__new__ creates each class normally, checks for rate_per_lb and stores the class in a registry. I saved the metaclass and base class as carriers.py so later examples can import them.

class CarrierMeta(type):
    registry = {}

    def __new__(mcs, name, bases, namespace):
        cls = super().__new__(mcs, name, bases, namespace)
        if bases:                                 # skip the base class
            if getattr(cls, "rate_per_lb", None) is None:
                raise TypeError(f"{name} must define rate_per_lb")
            mcs.registry[name.lower()] = cls
        return cls


class Carrier(metaclass=CarrierMeta):
    def quote(self, pounds):
        return round(pounds * self.rate_per_lb, 2)


class Ground(Carrier):
    rate_per_lb = 0.85


class Express(Carrier):
    rate_per_lb = 2.40


class Freight(Carrier):
    rate_per_lb = 0.52


print(sorted(CarrierMeta.registry))
for name, cls in CarrierMeta.registry.items():
    print(f"{name:<8} 48 lb -> ${cls().quote(48):,.2f}")

Output:

['express', 'freight', 'ground']
ground   48 lb -> $40.80
express  48 lb -> $115.20
freight  48 lb -> $24.96

Here’s Command Prompt running it:

Python metaclass Command Prompt output showing the registry with express, freight and ground classes and USD quotes for 48 lb
All three carriers registered themselves without a single manual registry line.

Nobody called a register function. Defining the class was enough, so 48 lb quotes at $40.80, $115.20 and $24.96. This is the same idea as a factory pattern in Python, except the lookup table fills itself.

mcs is the metaclass, much like self in a normal method. The if bases: check skips Carrier itself, which has no price.

Validate Class Attributes with a Python Metaclass

The registry’s nice, but validation is what you’ll value most. A new carrier without rate_per_lb now fails on import, not in production:

from carriers import Carrier

print("Defining Overnight...")


class Overnight(Carrier):
    flat_fee = 25.00


print("never printed")

Output:

Defining Overnight...
Traceback (most recent call last):
  File "C:\pyguides\overnight.py", line 6, in <module>
    class Overnight(Carrier):
        flat_fee = 25.00
  File "C:\pyguides\carriers.py", line 8, in __new__
    raise TypeError(f"{name} must define rate_per_lb")
TypeError: Overnight must define rate_per_lb
Command Prompt traceback where the Python metaclass raises TypeError: Overnight must define rate_per_lb at class definition
The class statement itself raises the TypeError, so the last print never runs.

The first print runs, then the class statement raises. No object was ever created. For checks on objects rather than classes, isinstance() and issubclass() usually do the job.

Metaclass __new__ vs __init__ vs __call__: What Runs When?

A metaclass can hook three moments. __new__ builds the class, __init__ finishes it, and __call__ runs every time you create an instance. I traced them on a small Invoice class:

class TraceMeta(type):
    def __new__(mcs, name, bases, ns):
        print(f"1. __new__  builds the class {name}")
        return super().__new__(mcs, name, bases, ns)

    def __init__(cls, name, bases, ns):
        print(f"2. __init__ finishes the class {name}")
        super().__init__(name, bases, ns)

    def __call__(cls, *args, **kwargs):
        print(f"3. __call__ makes a {cls.__name__} object")
        return super().__call__(*args, **kwargs)


class Invoice(metaclass=TraceMeta):
    def __init__(self, total):
        print(f"4. Invoice.__init__ sets total=${total:,.2f}")
        self.total = total


print("-- class is ready --")
inv = Invoice(1249.99)

Output:

1. __new__  builds the class Invoice
2. __init__ finishes the class Invoice
-- class is ready --
3. __call__ makes a Invoice object
4. Invoice.__init__ sets total=$1,249.99
Command Prompt output of a Python metaclass trace showing __new__ and __init__ at class creation and __call__ before Invoice.__init__
Steps 1 and 2 run once at definition; steps 3 and 4 run for each Invoice you create.

Use __new__ when you need to change the namespace before the class exists, and __init__ when the finished class is enough. The same split exists for normal objects, as __new__ vs __init__ in Python shows.

__call__ is the interesting one. Defining it on the metaclass decides what Invoice(...) returns, because calling a class is just calling an object (see Python callable()).

Build a Singleton with a Metaclass __call__

That hook gives you the classic singleton. The metaclass keeps one object per class and returns it on every later call:

class SingletonMeta(type):
    _instances = {}

    def __call__(cls, *args, **kwargs):
        if cls not in cls._instances:
            cls._instances[cls] = super().__call__(*args, **kwargs)
        return cls._instances[cls]


class AppConfig(metaclass=SingletonMeta):
    def __init__(self, region="us-east-1"):
        print(f"Loading config for {region}")
        self.region = region


a = AppConfig("us-east-1")
b = AppConfig("us-west-2")      # ignored: the object already exists
print(a is b, b.region)

Output:

Loading config for us-east-1
True us-east-1

The config loads once, and the second call returns the same object, still set to us-east-1. The __init__ body is skipped too, unlike a normal constructor in Python.

Pro Tip: if threads create the object, wrap the check in a threading.Lock. Often a module-level object is an even simpler singleton.

Control the Class Body with a Metaclass __prepare__

__prepare__ runs before the class body and returns the mapping Python fills with names. A dict subclass there can watch every assignment. This one catches a method pasted twice:

class NoDuplicates(dict):
    def __setitem__(self, key, value):
        if key in self and not key.startswith("__"):
            raise TypeError(f"{key!r} is defined twice in the class")
        super().__setitem__(key, value)


class StrictMeta(type):
    @classmethod
    def __prepare__(mcs, name, bases, **kwargs):
        return NoDuplicates()

    def __new__(mcs, name, bases, ns):
        return super().__new__(mcs, name, bases, dict(ns))


class PayrollReport(metaclass=StrictMeta):
    def total(self):
        return 5200.00

    def total(self):            # pasted twice by mistake
        return 0.00

Output:

Traceback (most recent call last):
  File "C:\pyguides\payroll_report.py", line 17, in <module>
    class PayrollReport(metaclass=StrictMeta):
    ...<4 lines>...
            return 0.00
  File "C:\pyguides\payroll_report.py", line 21, in PayrollReport
    def total(self):            # pasted twice by mistake
        return 0.00
  File "C:\pyguides\payroll_report.py", line 4, in __setitem__
    raise TypeError(f"{key!r} is defined twice in the class")
TypeError: 'total' is defined twice in the class

Without the metaclass, Python silently keeps the second total(), which returns $0.00. Here the definition fails with a clear message. Names starting with __ are skipped because Python sets a few of them for you.

__prepare__ must be a classmethod, because it runs before any class exists. __new__ receives the custom mapping, so I copy it into a plain dict.

Do You Really Need a Metaclass? __init_subclass__ and Class Decorators

Usually you don’t. Since Python 3.6, __init_subclass__ on a base class runs for every subclass, which covers registries and validation without any custom metaclass:

class Carrier:
    registry = {}

    def __init_subclass__(cls, **kwargs):
        super().__init_subclass__(**kwargs)
        if getattr(cls, "rate_per_lb", None) is None:
            raise TypeError(f"{cls.__name__} must define rate_per_lb")
        Carrier.registry[cls.__name__.lower()] = cls


class Ground(Carrier):
    rate_per_lb = 0.85


class Express(Carrier):
    rate_per_lb = 2.40


print(sorted(Carrier.registry), type(Ground))

Output:

['express', 'ground'] <class 'type'>

Same registry, same check, and type(Ground) is plain type. For changes to a single class, class decorators in Python are even lighter, and dataclasses generate __init__ and friends for you.

ToolRunsBest for
__init_subclass__Once per subclassRegistries, required attributes
Class decoratorOnce, on the decorated classAdding or wrapping methods
MetaclassClass creation and every instance call__prepare__, __call__, frameworks

I reach for a metaclass only when I need __prepare__, control o