Metaprogramming with Metaclasses in Python
Last Updated :
12 Apr, 2025
Metaprogramming in Python lets us write code that can modify or generate other code at runtime. One of the key tools for achieving this is metaclasses, which allow us to control the creation and behavior of classes.
What Are Metaclasses?
Metaclasses are classes that define how other classes are created. While regular classes act as blueprints for creating objects, metaclasses serve as blueprints for creating classes themselves. When we create a class in Python, it is, in fact, an instance of a metaclass. By default, every class in Python is an instance of the built-in type metaclass.
This whole meta thing can be summarized as - Metaclass create Classes and Classes creates objects.

The metaclass is responsible for the generation of classes, so we can write our custom metaclasses to modify the way classes are generated by performing extra actions or injecting code. Usually, we do not need custom metaclasses but sometimes it's necessary.
Creating custom Metaclass
To create our custom metaclass, our custom metaclass has to inherit type metaclass and usually override -
- __new__(): It's a method which is called before __init__(). It creates the object and returns it. We can override this method to control how the objects are created.
- __init__(): This method just initialize the created object passed as a parameter
We can create classes using the type() function directly. It can be called in following ways -
- When called with only one argument, it returns the type. We have seen it before in the above examples.
- When called with three parameters, it creates a class. Following arguments are passed to it -
- Class name
- Tuple having base classes inherited by class
- Class Dictionary: It serves as a local namespace for the class, populated with class methods and variables
Consider this example -
Python
def test(self):
print("This is Test class method!")
class Base:
def myfun(self):
print("This is inherited method!")
# Creating Test class dynamically using type()
Test = type('Test', (Base, ), dict(x="ankit", my_method=test))
print("Type of Test class: ", type(Test))
obj = Test()
print("Type of obj: ", type(obj))
obj.myfun()
obj.my_method()
print(obj.x)
OutputType of Test class: <class 'type'>
Type of obj: <class '__main__.Test'>
This is inherited method!
This is Test class method!
ankit
Explanation:
- test Method prints "This is Test class method!".
- Base Class has myfun method that prints "This is inherited method!".
- Dynamic Test Class is created with type(), inherits from Base, adds x="ankit" and my_method=test.
- Prints types of Test and obj, calls myfun() and my_method(), then prints x ("ankit").
Now let's create a metaclass without using type() directly. In the following example, we will be creating a metaclass MultiBases which will check if the class being created has inherited from more than one base class. If so, it will raise an error.
Python
class MultiBases(type):
# overriding __new__ method
def __new__(cls, clsname, bases, clsdict):
# if no of base classes is greater than 1 raise error
if len(bases)>1:
raise TypeError("Inherited multiple base classes!!!")
# else execute __new__ method of super class, ie. call __init__ of type class
return super().__new__(cls, clsname, bases, clsdict)
class Base(metaclass=MultiBases):
pass
class A(Base):
pass
class B(Base):
pass
class C(A, B):
pass
Output:
Traceback (most recent call last):
File "main.py", line 20, in <module>
class C(A, B):
File "main.py", line 6, in __new__
raise TypeError("Inherited multiple base classes!!!")
TypeError: Inherited multiple base classes!!!
Explanation: This code defines a custom metaclass MultiBases that restricts classes from inheriting from more than one base class. In the __new__ method, it checks if the class has more than one base class and raises a TypeError if so. The Base class uses this metaclass, and while A and B (subclasses of Base) don't raise any errors, C (inheriting from both A and B) triggers a TypeError.
Solving problems with metaclass
Some problems in Python can be solved using decorators or metaclasses. While decorators provide a simple solution for many tasks, there are certain situations where only metaclasses can provide a more efficient or scalable solution.
Problem: Debugging Class Methods
The task is to debug class methods, meaning every time a method is executed, it should print its fully qualified name before executing its body.
Solution 1: Using Method Decorators
Here's an implementation using method decorators to debug the methods of a class:
Python
from functools import wraps
def debug(func):
'''decorator for debugging passed function'''
@wraps(func)
def wrapper(*args, **kwargs):
print("Full name of this method:", func.__qualname__)
return func(*args, **kwargs)
return wrapper
def debugmethods(cls):
'''class decorator make use of debug decorator
to debug class methods '''
# check in class dictionary for any callable(method) if exist, replace it with debugged version
for key, val in vars(cls).items():
if callable(val):
setattr(cls, key, debug(val))
return cls
# sample class
@debugmethods
class Calc:
def add(self, x, y):
return x+y
def mul(self, x, y):
return x*y
def div(self, x, y):
return x/y
mycal = Calc()
print(mycal.add(2, 3))
print(mycal.mul(5, 2))
OutputFull name of this method: Calc.add
5
Full name of this method: Calc.mul
10
Explanation:
- debug Decorator: This decorator prints the fully qualified name of the method (func.__qualname__) and then calls the method.
- debugmethods Class Decorator: This decorator scans the class dictionary for callable methods and applies the debug decorator to them.
- Calc Class: The Calc class uses the @debugmethods decorator, so its methods (add, mul, and div) will print their fully qualified names when called.
Solution 2: Using a Metaclass
Now, let's look at a metaclass-based solution that automatically applies the debugging functionality to all subclasses of a base class.
Python
from functools import wraps
def debug(func):
'''decorator for debugging passed function'''
@wraps(func)
def wrapper(*args, **kwargs):
print("Full name of this method:", func.__qualname__)
return func(*args, **kwargs)
return wrapper
def debugmethods(cls):
'''class decorator make use of debug decorator to debug class methods '''
for key, val in vars(cls).items():
if callable(val):
setattr(cls, key, debug(val))
return cls
class debugMeta(type):
'''meta class which feed created class object to debugmethod to get debug functionality enabled objects'''
def __new__(cls, clsname, bases, clsdict):
obj = super().__new__(cls, clsname, bases, clsdict)
obj = debugmethods(obj)
return obj
# base class with metaclass 'debugMeta' now all the subclass of this will have debugging applied
class Base(metaclass=debugMeta):pass
# inheriting Base
class Calc(Base):
def add(self, x, y):
return x+y
# inheriting Calc
class Calc_adv(Calc):
def mul(self, x, y):
return x*y
# Now Calc_adv object showing debugging behaviour
mycal = Calc_adv()
print(mycal.mul(2, 3))
OutputFull name of this method: Calc_adv.mul
6
Explanation:
- debug Decorator: Same as in the first solution, it prints the fully qualified name of the method and calls it.
- debugmethods Class Decorator: This applies the debug decorator to all methods of a class.
- debugMeta Metaclass: The debugMeta metaclass overrides the __new__ method to automatically apply the debugmethods decorator to the class when it is created. This ensures that all methods in any class using this metaclass will have debugging enabled without needing to explicitly apply the decorator.
- Base Class: The Base class uses the debugMeta metaclass, so all subclasses will automatically have debugging applied to their methods.
When to use Metaclasses
Most of the time we do not use metaclasses, it's usually used for something complicated, but a few cases where we use metaclasses are -
- As we have seen in the above example, metaclasses propagate down the inheritance hierarchies. It will affect all the subclasses as well. If we have such a situation, then we should use metaclasses.
- If we want to change class automatically, when it is created, we use metaclasses
- For API development, we might use metaclasses
As quoted by Tim Peters
Metaclasses are deeper magic that 99% of users should never worry about. If you wonder whether you need them, you don't (the people who actually need them know with certainty that they need them, and don't need an explanation about why).
Similar Reads
Python Tutorial - Learn Python Programming Language Python is one of the most popular programming languages. Itâs simple to use, packed with features and supported by a wide range of libraries and frameworks. Its clean syntax makes it beginner-friendly. It'sA high-level language, used in web development, data science, automation, AI and more.Known fo
10 min read
Python Interview Questions and Answers Python is the most used language in top companies such as Intel, IBM, NASA, Pixar, Netflix, Facebook, JP Morgan Chase, Spotify and many more because of its simplicity and powerful libraries. To crack their Online Assessment and Interview Rounds as a Python developer, we need to master important Pyth
15+ min read
Python OOPs Concepts Object Oriented Programming is a fundamental concept in Python, empowering developers to build modular, maintainable, and scalable applications. By understanding the core OOP principles (classes, objects, inheritance, encapsulation, polymorphism, and abstraction), programmers can leverage the full p
11 min read
Python Projects - Beginner to Advanced Python is one of the most popular programming languages due to its simplicity, versatility, and supportive community. Whether youâre a beginner eager to learn the basics or an experienced programmer looking to challenge your skills, there are countless Python projects to help you grow.Hereâs a list
10 min read
Python Exercise with Practice Questions and Solutions Python Exercise for Beginner: Practice makes perfect in everything, and this is especially true when learning Python. If you're a beginner, regularly practicing Python exercises will build your confidence and sharpen your skills. To help you improve, try these Python exercises with solutions to test
9 min read
Python Programs Practice with Python program examples is always a good choice to scale up your logical understanding and programming skills and this article will provide you with the best sets of Python code examples.The below Python section contains a wide collection of Python programming examples. These Python co
11 min read
Python Introduction Python was created by Guido van Rossum in 1991 and further developed by the Python Software Foundation. It was designed with focus on code readability and its syntax allows us to express concepts in fewer lines of code.Key Features of PythonPythonâs simple and readable syntax makes it beginner-frien
3 min read
Python Data Types Python Data types are the classification or categorization of data items. It represents the kind of value that tells what operations can be performed on a particular data. Since everything is an object in Python programming, Python data types are classes and variables are instances (objects) of thes
9 min read
Input and Output in Python Understanding input and output operations is fundamental to Python programming. With the print() function, we can display output in various formats, while the input() function enables interaction with users by gathering input during program execution. Taking input in PythonPython input() function is
8 min read
Enumerate() in Python enumerate() function adds a counter to each item in a list or other iterable. It turns the iterable into something we can loop through, where each item comes with its number (starting from 0 by default). We can also turn it into a list of (number, item) pairs using list().Let's look at a simple exam
3 min read