In this article, we’re going to discuss metaclasses , why and when we should use them, and what the alternatives are. This is a fairly advanced Python topic and the following prerequisite is expected —
- OOP Concept in Python
- Decorators in Python
Note: This article is about Python 3.3 and up
In Python, everyone has some type associated with it. For example, if we have a variable that has an integer value, then its type — int. You can get the type of anything using the type () function.
Type of num is: "class ’int’" Type of lst is: "class ’list’" Type of name is: "class ’str’"
Every type in Python is defined by a class . So in the above example, unlike C or Java, where int, char, float are the main data types, in Python they are objects of class int or str. Thus, we can create a new type by creating a class of that type. For example, we can create a new type Student by creating a class Student .
Type of stu_obj is: "class ’__main __. Student’"
A class is also an object , and like any other object, it is an instance of something called a metaclass . The special type class creates these class objects. The class of type is the default metaclass , which is responsible for creating classes. For example, in the above example, if we try to figure out the type of the class Student , it turns out to be a type .
Type of Student class is: "class ’type’"
Since classes are also objects, they can be modified in the same way. We can add or subtract fields or methods in the class just like we did with other objects. For example —
This whole meta thing can be summarized as — Metaclass create Classes and Classes creates objects
The metaclass is responsible for generating classes, so we can write our own metaclasses to change the way we generate classes by performing additional actions or injecting code. We usually don’t need custom metaclasses, but sometimes we do.
There are problems for which metaclass and nonmetaclass solutions are available (often simpler), but in some cases only a metaclass can solve this problem. We will discuss such a problem in this article.
Creating our own metaclass
To create our own metaclass, our custom the metaclass must inherit the type metaclass and usually override —
- __new __ (): is the method that is called before __init __ ( ). Creates an object and returns it. We can override this method to control the creation of objects.
- __init __ (): this method simply initializes the created object passed as a parameter
We can create classes using the type () function directly. It can be called in the following ways —
- When called with only one argument, it returns a type. We’ve seen this before in the examples above.
- When called with three parameters, it creates a class. The following arguments are passed to it —
- Class name
- A tuple having base classes inherited from the class
- Class dictionary: serves as local a namespace for a class filled with class methods and variables.
Consider this example —
Traceback (most recent call last): File ""stdin"", line 2, in "module" File ""stdin"", line 8, in __new__ TypeError: Inherited multiple base classes !!!
Solving the metaclass problem
There are some problems that can be solved by both decorators (easily) and metaclasses. But there are several problems that can only be achieved with metaclasses. For example, consider a very simple code repetition problem.
We want to debug class methods, we need to ensure that whenever a class method is executed, it will print the fully qualified name before executing its body.
The very first solution that comes to our mind is — this is using decoration methods , below is some code example:
Full name of this method: Calc.add 5 Full name of this method: Calc.mul 10
This solution works great, but there is one problem: what if we want to apply this method decorator to all subclasses that inherit this class Calc . In this case, we must separately apply the decorator method to each subclass, as we did with the Calc class.
The problem is that if we have there are many such subclasses, then in this case we will not like adding a decorator to each separately. If we know in advance that every subclass should have this debug property, then we should look for a metaclass solution.
Take a look at this metaclass solution , the idea is that the classes will be generated normally and then immediately wrapped with the debug method decorator —
Full name of this method: Calc_adv.mul 6
When to use metaclasses
Most of the time we do not use metaclasses, they are like black magic and usually for something complex, but there are few cases where we use metaclasses:
- How do we seen in the above example, metaclasses propagate down the inheritance hierarchy. This will affect all subclasses as well. If this is our situation, then we have to use metaclasses.
- If we want to change the class automatically when it is created
- If you are an API developer, you can use metaclasses
According to 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).
- http://www.dabeaz.com/ py3meta / Py3Meta.pdf
- https: / /stackoverflow.com/questions/100003/what-is-a-metaclass-in-python
This article courtesy of Atul Kumar . If you are as Python.Engineering and would like to contribute, you can also write an article using contribute.python.engineering or by posting an article contribute @ python.engineering. See my article appearing on the Python.Engineering homepage and help other geeks.
Please post comments if you find anything wrong or if you’d like to share more information on the topic discussed above.