Table of contents
1.
Introduction
2.
Decorators
3.
Meta Classes
4.
Frequently Asked Questions
5.
Key Takeaways
Last Updated: Mar 27, 2024

Metaprogramming

Introduction

The word "metaprogramming" refers to a program's ability to understand or manipulate itself. Metaclasses are a type of metaprogramming that Python allows for classes. Metaclasses are a nebulous OOP idea that underpins almost all Python code. Whether you realize it or not, you're utilizing them.

It's beneficial to learn about Python metaclasses because it gives a better grasp of Python classes in general. You never know when you'll find yourself in one of those circumstances where you need to use your metaclass.

Decorators and Meta-classes are used to achieve metaprogramming in Python.

Must Recommended Topic, Floor Division in Python, Swapcase in Python

“ Recommended Topic, Python Round Function”.

Decorators

A decorator allows you to add additional functionality to a function without changing its structure.

For example, we have 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

print(add(3, 2))
print(sub(3, 2))
print(mul(3, 2))

Output:

5
1
6

We now want to print the function name and the parameters when the function is called. We can do this by adding the print statements.

Adding print/log statements to all three methods is the default method. However, this sounds like a lot of effort, and we'd have to change the bodies of each function as well.

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(3, 2))
print(sub(3, 2))
print(mul(3, 2))

Output:

add is called with parameter 3,2
5
sub is called with parameter 3,2
1
mul is called with parameter 3,2
6

Is it possible for us to do better? This can be accomplished by writing a decorator function and leaving the existing function body alone.

def decorator(func):
    def wrapper_function(*args):
        print("{0} is called with parameter {1}".format(func.__name__, args))
        return func(*args)
    return wrapper_function

@decorator
def add(x, y):
    return x + y
   
@decorator
def sub(x, y):
    return x - y

@decorator    
def mul(x, y):
    return x * y

print(add(3, 2))
print(sub(3, 2))
print(mul(3, 2))

Output:

add is called with parameter (3, 2)
5
sub is called with parameter (3, 2)
1
mul is called with parameter (3, 2)
6

In the above code, we have used the decorator function to add print functionality to all three functions without changing the original function body. Decorators are, in essence, functions that take functions as input and return other functions. The decorator accepts a function in the above code and returns a wrapper function that adds the print functionality to the original functions. Thus, we have seen how to implement metaprogramming using decorators.

You can compile it with online python compiler.

Meta Classes

In Python, meta-classes are different from regular classes in that they are unique types of classes. A meta-class specifies the behavior of both an ordinary class and its instance, whereas an ordinary class defines the behavior of its instance.

Metaclasses can add or remove a method to a regular class. The ‘type’ class is a special class in python that is a meta-class by default. All other classes inherit from the ‘type’ class.

For example, if we define a class ‘Calc’ with three methods and want to add functionality to debug all the methods in one class, we can use 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



def debugFunc(func):

    def wrapper(*args, **kwargs):
        print("{0} is called with parameters {1}".format(func.__qualname__, args[1:]))
        return func(*args, **kwargs)
   
    return wrapper


def debugAllFuncs(cls):

    for key, val in vars(cls).items():
        if callable(val):
            setattr(cls, key, debugFunc(val))
    return cls


class DebugClass(type):

    def __new__(cls, clsname, bases, clsdict):
        obj = super().__new__(cls, clsname, bases, clsdict)
        obj = debugAllFuncs(obj)
        return obj


class Calc(metaclass=DebugClass):
    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))

Also read,  Python filename extensions

Output:

Calc.add is called with parameters (2, 3)
5
Calc.sub is called with parameters (2, 3)
-1
Calc.mul is called with parameters (2, 3)
6

In the above example, we created a metaclass and wrote a decorator function to print the function that is called and its parameters. ‘Calc’ is inherited from ‘MetaDebugClass’. Therefore, its methods were by default decorated by the decorator function.

This way, we have added additional functionality to all the existing functions in the class. We can use meta-classes to achieve a lot more, such as adding a new function to the class and removing a function from the class. This way we can achieve metaprogramming using metaclasses in python.

Also See, Intersection in Python and Convert String to List Python.

Frequently Asked Questions

  1. What is Metaprogramming?
    Metaprogramming is a technique in which a program uses other programs as its input data. We can read, modify, or analyze other programs using metaprogramming. We can implement metaprogramming in python using decorators, metaclasses, etc.
  2. What are decorators in python?
    A decorator allows you to add additional functionality to a function without changing its structure.
  3. What is the difference between a metaclass and an ordinary class?
    Meta-classes are different from regular classes because they are unique types of classes. A meta-class specifies the behavior of both an ordinary class and its instance, whereas an ordinary class defines the behavior of its instance.

Key Takeaways

In this article, we studied in detail metaprogramming in python. We saw examples of metaprogramming using decorators and metaclasses. We hope that this blog has helped you enhance your knowledge of metaprogramming and if you would like to learn more, check out our articles on code studio. Do upvote our blog to help other ninjas grow. Happy Coding!

Live masterclass