Meta-Classes

Now that we’ve seen decorators, they are for decorating functions. But there is more to meta-programming than decorators, such as meta-classes.

Meta-classes are special types of classes, rather than ordinary classes in Python. Where an ordinary class defines behavior of its own instance, a meta-class defines the behavior of an ordinary class and its instance.

A meta-class can add or subtract a method or field to an ordinary class. Python has one special class, the type class, which is by default a meta-class. All custom type classes must inherit from the type class.

For instance, if we have class Calc , with three class methods, and we want to provide debug functionality to all the methods in one class then we can use a meta-class for this.

class Calc():

def add(self, x, y):

return x + y



def sub(self, x, y):

return x - y



def mul(self, x, y):

return x * y

First, we need to create a meta-class MetaClassDebug , with debug functionality, and make the Calc class inherit from MetaClassDebug .

And, when we call any method from the Calc class, it will get invoked with our debug_function .

def debug_function(func):



def wrapper(*args, **kwargs):

print("{0} is called with parameter {1}".format(func.__qualname__, args[1:]))

return func(*args, **kwargs)



return wrapper





def debug_all_methods(cls):



for key, val in vars(cls).items():

if callable(val):

setattr(cls, key, debug_function(val))

return cls





class MetaClassDebug(type):



def __new__(cls, clsname, bases, clsdict):

obj = super().__new__(cls, clsname, bases, clsdict)

obj = debug_all_methods(obj)

return obj





class Calc(metaclass=MetaClassDebug):

def add(self, x, y):

return x + y



def sub(self, x, y):

return x - y



def mul(self, x, y):

return x * y





calc = Calc()

print(calc.add(2, 3))

print(calc.sub(2, 3))

print(calc.mul(2, 3))





**************** output ****************



Calc.add is called with parameter (2, 3)

5

Calc.sub is called with parameter (2, 3)

-1

Calc.mul is called with parameter (2, 3)

6

Bingo! In the above snippet, we created a meta-class MetaClassDebug and wrote a new method which is responsible for creating an instance of class and applies our decorator function debug_function to the object (instance), which will get created for every class that inherits MetaClassDebug .

Calc is inherited from MetaClassDebug , hence every method has been decorated by debug_function from debug_all_methods .

This way, we can add new behavior to all the methods within a class and also control the instance creation of a class using a meta-class. We can achieve a lot with a meta-class, such as adding a method or field to class, removing a method or field from a class, and many more.

I wanted you to have a quick look at meta-programming in Python, so I wasn’t able to cover all the things in this post.

I hope that this article has helped you to familiarize yourself with the concept of meta-programming. Criticism is always welcome!