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
Programming Paradigms in Python
Paradigm can also be termed as a method to solve some problems or do some tasks. A programming paradigm is an approach to solve the problem using some programming language or also we can say it is a method to solve a problem using tools and techniques that are available to us following some approach
4 min read
Python Metaclass __new__() Method
In Python, metaclasses provide a powerful way to customize the creation of classes. One essential method in metaclasses is __new__, which is responsible for creating a new instance of a class before __init__ is called. Understanding the return value of __new__ in metaclasses is crucial for implement
3 min read
Getting Started with Python Programming
Python is a versatile, interpreted programming language celebrated for its simplicity and readability. This guide will walk us through installing Python, running first program and exploring interactive codingâall essential steps for beginners. Install PythonBefore starting this Python course first,
3 min read
Last Minute Notes (LMNs) - Python Programming
Python is a widely-used programming language, celebrated for its simplicity, comprehensive features, and extensive library support. This "Last Minute Notes" article aims to offer a quick, concise overview of essential Python topics, including data types, operators, control flow statements, functions
15+ min read
Integrating Java with Python
While programming in a language, a developer may feel the need to use some functionality that may have better support in another language. For example, suppose, an application has already been developed in Java, and we wish to use it in Python code. To invoke an existing Java application in Python,
4 min read
8 Tips For Object-Oriented Programming in Python
OOP or Object-Oriented Programming is a programming paradigm that organizes software design around data or objects and relies on the concept of classes and objects, rather than functions and logic. Object-oriented programming ensures code reusability and prevents Redundancy, and hence has become ver
6 min read
Python | Implementing Dynamic programming using Dictionary
Dynamic Programming is one way which can be used as an optimization over plain recursion. Wherever we see a recursive solution that has repeated calls for the same inputs, we can optimize it using Dynamic Programming. The idea is to simply store the results of subproblems so that we do not have to r
3 min read
Python MetaClasses
The key concept of python is objects. Almost everything in python is an object, which includes functions and as well as classes. As a result, functions and classes can be passed as arguments, can exist as an instance, and so on. Above all, the concept of objects let the classes in generating other c
9 min read
How to Create a Programming Language using Python?
In this article, we are going to learn how to create your own programming language using SLY(Sly Lex Yacc) and Python. Before we dig deeper into this topic, it is to be noted that this is not a beginner's tutorial and you need to have some knowledge of the prerequisites given below. PrerequisitesRou
7 min read
Mathematics Tricks For Competitive Programming In Python 3
Here are some of the exciting features that Python 3.8 provides for programmers in competitive programming. These new features are related to some math functions and algorithms that are frequently used by competitive programmers. The implementation of these features has the time complexity which is
3 min read