Have you ever wondered how Python classes themselves are created? Have you ever heard of metaclass
?
Whether you’re customizing class behavior, enforcing coding patterns, or building frameworks, metaclass
offer incredible power and flexibility. In this blog, we’ll look into the layers and explore one of Python’s most advanced and intriguing concepts: metaclass
.

We’ll start by understanding the difference between a class and a metaclass
, along with what Python’s type
exactly is. From there, we’ll dive into creating dynamic classes using type, and look into practical, hands-on examples. You’ll learn how to design your own custom metaclass
.
By the end of this blog, you’ll not only understand what metaclass
es are but also how to use them in your own projects with the help of 5 use-case examples.
Let’s get started!
What Will You Learn? [hide]
metaclass
in Python
How metaclass
is different from class in Python?
Class in Python defines how an object of that class will look like when they are instantiated. Class is used to define the behavior and data of individual objects.
metaclass
in Python defines how a class will work when they are created. They are used to customize classes.
Objects are instances of class, but the classes are instances of those metaclass
in Python.
Remember everything in Python is an object? And that is what this means. Even the classes are objects coming from some other class. We all have tried to check the type of variables, right?
a = 20
print(type(a))
Output
<class 'int'>
We expected this, right?
But have you ever tried to look to what is the type of int
, str
and other types in Python?
print(type(int))
Output
<class 'type'>
It is <class 'type'>
… What does that mean?
It means the int
is of type
class. What is type
?
If you are beginner in Python, you might be shocked to see this… type
is a metaclass
in Python that every class in Python automatically gets as it’s metaclass
.
Yes… Every class has metaclass
, even if you never mention it. And it looks something like this –
class A(metaclass=type):
pass
Now, what is the type of type
? It is <class 'type'>
itself.
So, let’s simplify the things with a simple diagram.

Creating Dynamic Class Using `type`
Now we understood the type
in depth, let’s see what we can use it for.
type
can be used to create dynamic classes given that you want to build classes on the fly with some base classes that you want it to inherit and some attributes and methods.
Why would you need to create dynamic classes?
If you are building a platform that supports dynamic forms (Users can create their own forms with different fields) then you have to create tables on the database on the fly to cater those use cases as you don’t know beforehand what type of forms with what type of fields they are going to create.
You will have some data that defines that form like the following –
sample_form_data = {
"form_id": "form_1",
"fields": [
"field1",
"field2",
"field3"
],
"field1": {
"id": "field1",
"type": "text",
"name": "Field 01",
},
"field2": {
"id": "field2",
"type": "text",
"name": "Field 02",
},
"field3": {
"id": "field3",
"type": "text",
"name": "Field 03",
}
}
You can create columns based on the fields, map the field types to proper db types, convert them to sql columns and list down all the columns.
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column
from sqlalchemy.dialects.postgresql import VARCHAR
Base = declarative_base()
class DictMixin:
def __setitem__(self, key, value):
setattr(self, key, value)
def __getitem__(self, key):
return getattr(self, key)
def generate_columns(fields):
columns = []
for field in fields:
columns.append(get_column(field))
return columns
DB_TYPES = {
'text': VARCHAR
}
def get_column(field):
field_type = DB_TYPES[field['type']]
column = Column(
field_type,
name=field['name']
)
return column
form_columns = generate_columns(sample_form_data['fields'])
Now creating the dynamic class is left… For that you have to know the syntax of type
.
type(name, bases, dict)
– name
is the class name, bases
is a tuple of all classes that this class will inherit and dict
contains all the attributes this class will have.
So, let’s create the dynamic table for this form-
form_table = type(sample_form_data['form_id'], (Base, DictMixin), {'__tablename__': sample_form_data['form_id'], **form_columns})
Cool, right?
Now, you know how type
works and how you can create classes on the fly with it. Let’s dive into how you can create your metaclass.
Create your own metaclass
To create your own metaclass, you just create another class which inherits `type` because as we know this is the defined metaclass in Python.
Then you just create another class and do the following –
class MetaClass(type):
pass
class A(metaclass=MetaClass):
pass
Fairly simple code but could be really useful depending on the use cases.
metaclass
becomes useful when you want to customize the classes generated from this metaclass
.
For that you need to be clear about the concept that metaclass
creates the class, which means – when you write a class, it is created from metaclass.
What do I mean by that?
class MetaClass(type):
def __new__(cls, name, bases, dict):
print(cls, name)
return super().__new__(cls, name, bases, dict)
class Base(metaclass=MetaClass):
pass
class Child(Base):
pass
Output
<class '__main__.MetaClass'> Base
<class '__main__.MetaClass'> Child
In this example, you can see that the output comes 2 times. One for Base
and other for Child
.
__new__
is a special method in Python class which is invoked when an object is created out of the class and __init__
is called after that for object initialization.
We are seeing 2 messages for Base
and Child
as 2 objects were created from the MetaClass
class.
Now, you know how to create your own metaclass
. Let’s dive deeper into multiple use cases where metaclass
es could be useful.
Python metaclass
use-cases
There are multiple use-cases where metaclass in Python could be useful.
Enforcing Class-Level Constraints
Sometimes you might want to make sure that the classes follow certain rules while being implemented.
You might have seen this error multiple times if you work with some plugin, or some libraries.
They make sure that some functions are implemented in the class, and if you don’t implement them, then they throw error.
This kind of mechanism could be achieved by metaclass.
Here you are building a plugin system where every plugin class must implement a run
method.
class PluginMeta(type):
def __new__(cls, name, bases, dict):
if 'run' in dict.keys():
raise TypeError(f"Class {name} must implement 'run' method.")
return super().__new__(cls, name, bases, dict)
class BasePlugin(metaclass=PluginMeta):
pass
class MyPlugin(BasePlugin): # Runs fine
def run(self):
print("Running MyPlugin")
class BrokenPlugin(BasePlugin): # Throws error
pass
Output
TypeError: Class MyPlugin must implement 'run' method.
Automatically Register Subclasses
If you want to keep track of all the classes that are deriving your base class then you can use metaclass.
Here, you are building a component that needs to maintain all available components except BaseComponent
.
class RegistryMeta(type):
registry = {}
def __new__(cls, name, bases, dct):
new_class = super().__new__(cls, name, bases, dct)
if name != 'BaseComponent': # Avoid registering the base class
cls.registry[name] = new_class
return new_class
class BaseComponent(metaclass=RegistryMeta):
pass
class ComponentA(BaseComponent):
pass
class ComponentB(BaseComponent):
pass
print(RegistryMeta.registry)
Output
Output: {'ComponentA': <class '__main__.ComponentA'>, 'ComponentB': <class '__main__.ComponentB'>}
Dynamic Customization
Let’s say you want to make sure that all the classes contain a particular method, then you can create your own metaclass
that contains that method and use this metaclass
on every class where you want that method.
Here you can include to_dict
to all classes of your API.
class AutoDictMeta(type):
def __new__(cls, name, bases, dct):
def to_dict(self):
return {key: value for key, value in self.__dict__.items()}
dct['to_dict'] = to_dict
return super().__new__(cls, name, bases, dct)
class APIBase(metaclass=AutoDictMeta):
pass
class User(APIBase):
def __init__(self, username, email):
self.username = username
self.email = email
u = User("johndoe", "johndoe@example.com")
print(u.to_dict())
Output
{'username': 'johndoe', 'email': 'johndoe@example.com'}
Singleton Pattern
Sometimes you might want to make sure that there is only one instance of your class and user can’t make any more instances of that class after the first one.
This is called singleton pattern, and we can achieve it using metaclass.
Here you are building a global configuration class that needs to be instantiated only once.
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 Config(metaclass=SingletonMeta):
def __init__(self, value):
self.value = value
c1 = Config("first")
c2 = Config("second")
print(c1.value)
print(c2.value)
print(c1 is c2)
Output
first
first
True
Adding Debug Information
If you want to debug your code and want to show some message before you run any class related method or attribute, you can use metaclass to log all the creation of your classes for debugging purpose.
Here you can track all class definitions in your application.
class DebugMeta(type):
def __new__(cls, name, bases, dct):
print(f"Creating class {name} with attributes {list(dct.keys())}")
return super().__new__(cls, name, bases, dct)
class DebugBase(metaclass=DebugMeta):
pass
class Example(DebugBase):
foo = 42
def bar(self):
pass
Output
Creating class Example with attributes ['__module__', '__qualname__', 'foo', 'bar']
Conclusion
As Peter Norvig famously said, “Metaclasses are deeper magic than 99% of users should ever need. If you wonder whether you need them, you don’t.”
This encapsulates the essence of metaclass
: they are a powerful, yet niche tool, designed for scenarios where you need to customize or control class creation in ways standard inheritance and decorators cannot achieve.
metaclass
shine when building frameworks, enforcing patterns, or generating dynamic and complex class hierarchies. They help you to abstract away repetitive class behaviors, making your codebase cleaner and more maintainable.
Want to dive deep into other Python concepts, but don’t know which one to start? Read this blog where I discuss about the essential topics of Python that you must master.
Happy Coding 🖥️
References: