![]() |
VOOZH | about |
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.
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.
👁 Image
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.
To create our custom metaclass, our custom metaclass has to inherit type metaclass and usually override -
We can create classes using the type() function directly. It can be called in following ways -
Consider this example -
Type of Test class: <class 'type'> Type of obj: <class '__main__.Test'> This is inherited method! This is Test class method! ankit
Explanation:
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.
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.
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.
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.
Here's an implementation using method decorators to debug the methods of a class:
Full name of this method: Calc.add 5 Full name of this method: Calc.mul 10
Explanation:
Now, let's look at a metaclass-based solution that automatically applies the debugging functionality to all subclasses of a base class.
Full name of this method: Calc_adv.mul 6
Explanation:
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 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).