Metaprogramming with Metaclasses in Python

Metaprogramming might sound new, but if the user has ever worked with Decorators or metaclasses, they have used metaprogramming in their projects. Therefore, we can say that metaprogramming is the program that is used for manipulating the program.

In this tutorial, we will discuss the metaclasses and their uses and what their alternatives are. As this is an advanced topic of Python, the users are advised to revise the basic concepts of "Decorators in Python" and "OOPS concepts in Python" before starting this tutorial.

Metaclasses

In Python, every module or function is associated with some type. For example, if a user has a variable that has the integer value, then it is associated with an "int" type. The user can know the type of anything by using the type() method.

Example:

Output:

Type of number is: <class 'int'>
Type of list is: <class 'list'>
Type of name is: <class 'str'>

Explanation -

Every type in Python is defined by class. Therefore, in the above example, they are the object of 'int,' 'list' or 'str' class type, unlike C language or Java language in which int, char, and float are the primary data types. The users can create the new type by creating the class of that type. For example, we can create a new type of Object by City class.

Example:

Output:

Type of City_object is: <class '__main__.City'>

The class in Python is also an object, and therefore, like other objects, it is an instance of Metaclass. The metaclass is a special class type which is responsible for creating classes and Class object. So, for example, if the user wants to find the type of the "City" class, they will find out that it is "type."

Example:

Output:

Type of City class is: <class 'type'>

As the classes are also an object, we can modify them. For example, the user can add or subtract fields or functions in the class as we do with other objects.

Example:

Output:

65
Pune

We can summarize the whole metaclass as:

Metaclass is used for creating Classes, and these classes can create the objects.

Metaprogramming with Metaclasses in Python

The metaclass is responsible for creating the classes so the user can write their customized metaclasses by inserting code or extra actions for modifying the way classes are created. Usually, the user does not need the customized metaclass, but in exceptional cases, it is necessary.

There are some issues that can be resolved by using metaclass or non-metaclass, but there are some cases in which the only metaclass is used for resolving them.

How to Create Customized Metaclass

For creating the customized metaclass, the user customized metaclass has to inherit type metaclass and usually override, such as:

  • __new__(): The __new__() function is classes before __int__() function. this is used for creating the object and return it. The user can override this function for controlling how the objects are created.
  • __int__(): The __int__() function is used for initializing the created object which is passed as the parameter.

The users can create the classes by using the type() method directly. The type() method can be called in the following ways:

  1. As shown in the previous example, the user can call it with one argument, and it will return the type.
  2. The user can call it in three parameters. It will create the class. The following arguments are passed in it:
    • Name of class
    • Pass the tuple which has base classes inherited by class
    • Class dictionary: This will be served as the local namespace for class, which is populated with functions and variables.

Example:

Output:

The Type of City class:  <class 'type'>
 The Type of City_object:  <class '__main__.City'>
This is a inherited method!
This is City class method!
Mark Jackson

Now, let's see how to create a metaclass without using the type() function directly. For example, we will create the metaclass called MultiBases, which will check if the class being created has inherited from more than one base class. If so, it will raise an error.

Example:

Output:

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-2-409c90c285d5> in <module>
     29     pass
     30 # This will raise an error!
---> 31 class S(P, Q, R):
     32     pass

<ipython-input-2-409c90c285d5> in __new__(city, city_name, bases, citydict)
      6         # it will raise an error
      7         if len(bases)>1:
----> 8             raise TypeError("There are inherited multiple base classes!!!")
      9 
     10         # else it will execute the __new__() function of super class, that is

TypeError: There are inherited multiple base classes!!!

How to Solve the Problem with Metaclass

There are some problems that users can solve by using metaclass and decorators. But there are some problems whose solution can be found only by using metaclass. Such as, the user wants to debug the class function, what they want is that whenever the class function is executed, it will print their full qualified name before executing their body.

Example:

Output:

The full name of this method: Calculator.add
13
The full name of this method: Calculator.mul
42
The full name of this method: Calculator.div
3.0

Explanation -

The above solution is working fine, but there is one problem, which is the user wants to apply the decorator method to all the subclasses which have inherited the "Calculator" class. So in that scenario, the user has to apply the decorator method separately to each subclass, just like we did in the above example for the Calculator class.

Now, the actual problem is there can be loads of subclasses of the class, and applying the decorator method individually to each subclass is challenging and a time consuming process. So, to resolve this problem, the user must make sure that each subclass has this debug property, they should look for the metaclass based solution.

Example:

We will create the class normally and then immediately we will wrap up by using the debug method decorator:

Output:

The full name of this Function: Calculator.add
10
The full name of this Function: Calculator_adv.mult
21

When the user should use Metaclass

Users don't use metaclass most often, as metaclasses are mainly used for complicated situations. But there are few cases where the users can use the metaclass:

  • As shown in the above example, Metaclass is used for generating down the inheritance hierarchies. This will affect all the subclasses also. If the user has such situations, then they can use the metaclass.
  • If the user wants to change the class automatically, they can use a metaclass when it is created.
  • If the user is an application programming interface developer, they can use metaclasses for that purpose.

Conclusion

In this tutorial, we have discussed metaclasses, how to customize the metaclass, and how users can use them to solve problems and complicated programming and their alternatives.






Latest Courses