Simplifying the Code: Meta-Programming in Python

A short tutorial about streamlining your code with Decorators and Meta-classes

Meta-programming in Python is a tough, albeit interesting, nut to crack.  This article is a guide for those who want to learn about two crucial features of meta-programming – decorators and meta classes.

What Is Meta-Programming?

In short, meta-programming is the act of writing code that manipulates code. However, without understanding the concept, the above one-liner probably won’t excite the grey cells. Let’s delve deeper to understand meta-programming in the context of Python.

Meta-programming in Python can be stated as: “Meta-programming is an act of building functions and classes that can manipulate code by modifying, wrapping existing code, or generating code.” However, this may seem really difficult to understand at one go. Therefore, it’s necessary to get familiar with the relevant terms.

There are two vital elements to achieve meta-programming in Python:

  • Decorators
  • Meta-classes

Decorators:

A decorator is a way of adding new functionality to an existing function without modifying its original structure.

Consider the following three functions:

def add(x, y):return x + y

def sub(x, y):

return x – y

def mul(x, y):

return x * y

Broadly speaking, a decorator helps to avoid repetition when printing the function name and parameter values each time the function is called.

With reference to the code block above, consider a situation in which we need to print the function name and parameter values when the function is called. This should be applicable to all three functions above.

The native way is to add print/log statements to all three functions. However, this is repetitive work and we would also need to modify each function body.

Here’s what that would look like:

def add(x, y):print(“add is called with parameter {0},{1}”.format(x,y))

return x + y

def sub(x, y):

print(“sub is called with parameter {0},{1}”.format(x,y))

return x – y

def mul(x, y):

print(“mul is called with parameter {0},{1}”.format(x,y))

return x * y

print(add(5,3))

print(sub(5,3))

print(mul(5,3))

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

add is called with parameter 5, 3

8

sub is called with parameter 5, 3

2

mul is called with parameter 5, 3

15

It is clear that the same output can be achieved in a more efficient manner. To do this, we need to write a decorator function. The proper use of a decorator can cut down the series of mundane tasks of modifying each function body.

def my_decorator(func):def wrapper_function(*args):

print(“{0} is called with parameter {1}”.format(func.__name__, args))

return func(*args)

return wrapper_function

@my_decorator

def add(x, y):

return x + y

@my_decorator

def sub(x, y):

return x – y

@my_decorator

def mul(x, y):

return x * y

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

add is called with parameter (5, 3)

8

sub is called with parameter (5, 3)

2

mul is called with parameter (5, 3)

15

In the above code snippet, my_decorator is a decorator function. We decorate all three functions with @my_decorator and we have not touched the existing function body to add this print functionality.

Therefore, a decorator function is, at its core, a higher-order function that take a function as an argument and returns another function.

In the above code, my_decorator takes a function as an argument and returns wrapper_function as a result. wrapper_function adds print functionality to func.

Now, let’s switch to another crucial element of meta-programming.

Meta-Classes:

A special class in itself, a meta-class would be best described as the class of a class. Instead of defining the behaviour of its own instances like an ordinary class, a meta-class defines the behaviour of an ordinary class and its instance. A class, in this case, would thus be an instance of a meta-class.

Using a meta-class, methods or fields can be added or subtracted from 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 typeclass . In this example, the class calc has three class methods. A meta-class can be used to provide a functionality such as debug, to all the methods at once.

class Calc():def add(self, x, y):
return x + ydef sub(self, x, y):
return x – ydef mul(self, x, y):
return x * yTo do this, first, we need to create a meta-class MetaClassDebug, with debug functionality, and make the Calc class inherit from MetaClassDebug. When we call any method from the Calc class, it will be 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

In the above code, the Calc class has been written to inherit from the meta-class MetaClassDebug, which has a debug functionality. The new method under meta-class MetaClassDebug creates an instance of the class and applies the decorator function   debug_function to the instance. An instance is created for every class that inherits from the MetaClassDebug. So, when any method from the Calc class is called, it is invoked with the decorator function   debug_function.

With meta-classes, not only can new behaviour be added to all methods within a class, but the instance creation of a class can also be controlled. With meta-classes, methods and fields can also be added or removed from classes.

Conclusion:

This article described decorators and meta-classes, two types of meta-programming in Python. If you need some guidance with fine-tuning your organization’s programming capabilities, please reach out to us at marketing@nitorinfotech.com. Nitor’s dedicated technology experts and programmers can help throughout your journey, from initial assessment to monitoring.

About Saurabh Kukade

Saurabh is an adept software developer, having specialization on Java, Python, and Haskell technology. He has proven mastery in programming, blogging, and backend software development on varied technology platforms and business domains. Being a blogger on medium.com – the online publishing platform, he has shown expertise in publishing several technical blogs on the channel, known as ‘Better Programing’.

Leave a Reply

Your email address will not be published. Required fields are marked *