In Python, classes are themselves objects, created at runtime by calling something — and that something is type, the default metaclass. Metaclasses let you customize class creation itself. They're rarely needed in application code, but understanding them demystifies how ORMs, ABCMeta, and Enum work internally.
1Classes Are Objects, and type Is Their Class
In Python, the statement 'everything is an object' extends further than most languages: classes themselves are objects, instances of some other class. type(Product()) returning <class '__main__.Product'> is unsurprising — an instance's type is its class. type(Product) returning <class 'type'> is the more surprising half: Product, the class itself, is an instance of type. type is the default metaclass — literally, the class whose instances are classes.
This is not an abstract philosophical point; it's directly demonstrable. class Product: price = 10 is executable syntax for something you can also write out explicitly: Product = type("Product", (), {"price": 10}), calling type directly with three arguments — the class's name as a string, a tuple of base classes, and a dict-like namespace mapping attribute names to values. The class keyword is convenient syntax for exactly this call; both produce an identical, fully functional class object.
This explains why every class, without exception, has a type(SomeClass) — because every class, unless told otherwise, is created by calling type(...), the same way every ordinary object is created by calling its class. The chain has to bottom out somewhere: type(type) is type itself, the one genuinely special case in this otherwise fully general system.
class Product:
pass
print(type(Product())) # <class '__main__.Product'> — an instance's type is its class
print(type(Product)) # <class 'type'> — a CLASS's type is 'type' itself<class 'type'>
2A Metaclass Customizes What class Does
If class Product: ... is sugar for calling type("Product", bases, namespace), then the natural next question is: can you substitute something other than type for that call? Yes — that's precisely what class Config(metaclass=UppercaseAttrMeta): does. UppercaseAttrMeta, itself a subclass of type (metaclasses are almost always type subclasses, not built from scratch), overrides __new__ to receive the exact same three arguments — name, bases, namespace — that type() would normally receive directly, transform them however it likes, and then delegate to super().__new__() to actually construct the resulting class.
In UppercaseAttrMeta, the namespace dict (every name defined in the class body, like debug = True) is rewritten so every non-dunder key is uppercased before the class is actually built — which is why Config.DEBUG exists, even though the source code wrote debug. This kind of transformation happens once, at class-definition time, not per-instance — it's fundamentally different from a decorator, which wraps an already-fully-formed function or class rather than participating in how the class body's contents are assembled in the first place.
This is exactly the mechanism real frameworks use under the hood: Django's ModelBase metaclass collects field definitions written in a model's class body and builds database schema metadata from them; abc.ABCMeta (behind abc.ABC) tracks which abstract methods a subclass has and hasn't implemented, raising TypeError at *instantiation* time if any remain unimplemented; enum.EnumMeta is what makes class Color(Enum): RED = 1 produce the specialized Enum member objects instead of plain integer class attributes.
# These two are equivalent:
class Product:
price = 10
Product = type("Product", (), {"price": 10})
print(Product.price) # 10True — uppercased by the metaclass at class-creation time
3When (Rarely) to Reach for a Metaclass Yourself
The near-universal advice about metaclasses, echoed by core Python developers, is: if you're not sure whether you need one, you don't. The overwhelming majority of problems that look like they need a metaclass — validating class attributes, auto-registering subclasses, adding common methods — are solved more simply and more readably with a class decorator (applying @decorator to the class, which just runs a function against an already-built class object) or with __init_subclass__, a regular classmethod hook (added in 3.6) that runs automatically whenever a class is subclassed, without requiring a custom metaclass at all.
__init_subclass__ in particular covers a large fraction of what used to require a metaclass: class Plugin: def __init_subclass__(cls, **kwargs): registry.append(cls) automatically registers every subclass of Plugin the moment it's defined — the exact 'auto-registration' use case that historically motivated a lot of metaclass code, now achievable with a single classmethod and no metaclass at all.
The legitimate remaining use cases for a real metaclass are narrow and mostly framework-level: you need to intercept and transform the *namespace itself* before the class exists (as UppercaseAttrMeta does), or you're building something like an ORM base class or ABCMeta where the class-creation process itself needs deep customization that __init_subclass__ genuinely cannot express. For everyday application code, understanding metaclasses to *read* framework source code confidently is the realistic, valuable goal — writing your own should be a deliberate, rare decision.
class UppercaseAttrMeta(type):
def __new__(mcs, name, bases, namespace):
uppercase_ns = {
(key.upper() if not key.startswith("__") else key): value
for key, value in namespace.items()
}
return super().__new__(mcs, name, bases, uppercase_ns)
class Config(metaclass=UppercaseAttrMeta):
debug = True
print(Config.DEBUG) # True — the metaclass uppercased it at class-creation timeCovers most 'I thought I needed a metaclass' cases without one
4Step-by-Step Breakdown
type(5) is int. But what is type(int)? The answer — type itself — is the entire foundation of metaclasses.
Every value has a type — including classes themselves. type(MyClass) reveals what actually created MyClass.
Checkpoint: What does type(Product) return, where Product is a class (not an instance)?
- →<class 'type'> — because type is the metaclass that created Product
- →<class 'object'> — because every class ultimately inherits from object
class Product: pass is itself sugar for calling type() directly — the three-argument form: name, bases, namespace.
A metaclass is a class whose instances are themselves classes. Subclass type to customize what happens when a class is created.
Checkpoint: What is a metaclass, in one sentence?
- →A class whose instances are themselves classes — it customizes class creation
- →Another name for an abstract base class
That completes Advanced Python — you now understand the core mechanisms (decorators, generators, iterators, context managers, descriptors, magic methods, and class creation) that professional Python code and frameworks are built on top of. Next, we look at how to structure that code into a real project.
Inject a Real Class Attribute via Metaclass. Finish Meta.__new__(): a metaclass's __new__ runs when the class itself is created.
Level Up 🚀
Advanced cheat sheets, SEO tricks, and interview prep for this topic.
Browser Support
Fully supported (via server-side Python execution).
Fully supported (via server-side Python execution).
Fully supported (via server-side Python execution).
Fully supported (via server-side Python execution).
Best Practices
Reach for __init_subclass__ or a class decorator before writing a custom metaclass
Both cover the vast majority of real use cases (validation, registration, adding shared behavior) with far less complexity and no risk of metaclass conflicts during multiple inheritance.
Learn metaclasses to read framework internals, not to write your own by default
Understanding ABCMeta, Django's ModelBase, and Enum's EnumMeta as ordinary applications of the type() protocol demystifies a lot of 'magic' framework behavior — that's the primary practical payoff for most engineers.
Frequent Bugs
Reaching for a custom metaclass to solve a problem (like auto-registering subclasses or validating class attributes) that __init_subclass__ or a class decorator would solve more simply.
Default to __init_subclass__ for 'run code whenever a subclass is defined' needs, and a class decorator for 'transform an already-built class' needs. Reserve metaclasses for genuine class-creation-time namespace manipulation.
Real-World Examples
Auto-Registering Plugin Subclasses Without a Metaclass
A plugin system needs every subclass of a Plugin base class to automatically register itself in a central registry, the classic use case historically solved with metaclasses — but achievable more simply here.
class Plugin:
registry: list[type] = []
def __init_subclass__(cls, **kwargs):
super().__init_subclass__(**kwargs)
Plugin.registry.append(cls)
class CsvExporter(Plugin):
pass
class JsonExporter(Plugin):
pass
print(Plugin.registry) # [CsvExporter, JsonExporter] — no metaclass needed