A Practical Guide to Metaprogramming in Python
Tags: programming, webdev, python, beginners
Published: 2023-07-16 18:25:22

## What is metaprogramming?

Metaprogramming is a programming technique where a program can modify or generate code at runtime. It allows developers to write code that can analyze, modify, or create other code. 

> In other words, metaprogramming is a way of writing programs that manipulate programs.

Metaprogramming is supported in a lot languages including Python, JavaScript, Ruby, Clojure, Julia and Java (even though it is considered to be a static and verbose language!)

![Relationship between structural and process concepts of metaprogramming. 
](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/779oi29cklb7mvhuk3vn.png)

Fig: Relationship between structural and process concepts of metaprogramming. Image from [Research Gate](https://www.researchgate.net/publication/267704068_Taxonomy_of_The_Fundamental_Concepts_of_Meta-Programming)

## Where is metaprogramming used?

Metaprogramming is useful for a lot of use cases. Some of them include

- Frameworks and Libraries
- Code Generation
- Template Engines

## Metaprogramming in Python

Metaprogramming in Python allows you to write code that can manipulate code itself, creating new code, modifying existing code, or analyzing code structures. Python provides several mechanisms for metaprogramming. Let's explore each of them with code examples:

### 1. Decorators

Decorators are functions that modify the behavior of other functions or classes. They wrap the target function or class and provide additional functionality. Decorators use the @ symbol and can be applied to functions or classes. Here's an example:

```py
def uppercase_decorator(func):
    def wrapper(*args, **kwargs):
        result = func(*args, **kwargs)
        return result.upper()
    return wrapper

@uppercase_decorator
def greet(name):
    return f"Hello, {name}!"

print(greet("Karishma"))
```  

Output

```py
HELLO, KARISHMA!
```
In the above example, the `uppercase_decorator` modifies the behavior of the `greet` function by converting its return value to uppercase.

### 2. Metaclasses

Metaclasses allow you to define the behavior of classes. They act as the blueprint for creating classes and can modify class creation and behavior. Here's an example:

```py
class MetaClass(type):
    def __new__(cls, name, bases, attrs):
        uppercase_attrs = {key.upper(): value for key, value in attrs.items() if not key.startswith('__')}
        return super().__new__(cls, name, bases, uppercase_attrs)

class MyClass(metaclass=MetaClass):
    message = "Hello, World!"

print(MyClass.MESSAGE)
```

Output

```py
Hello, World!
```

In the above example, the `MetaClass` metaclass modifies the attributes of the `MyClass` class by converting them to uppercase.

### 3. Function and Class Decorators

Besides using decorators with the @ symbol, you can also apply decorators using the function or class syntax. Here's an example:

```py
class CubeCalculator:
	def __init__(self, function):
		self.function = function

	def __call__(self, *args, **kwargs):

		# before function
		result = self.function(*args, **kwargs)

		# after function
		return result

# adding class decorator to the function
@CubeCalculator
def get_cube(n):
	print("Input number:", n)
	return n * n * n

print("Cube of number:", get_cube(100))
```

Output

```py
Input number: 100
Cube of number: 1000000
```

In the above example, we define a class decorator `CubeCalculator` that wraps the function `get_cube` to perform additional actions before and after its execution, and then applies the decorator to the `get_cube` function. When `get_cube` is called with an input number, it prints the input number, calculates its cube, and returns the result.

### 4. Dynamic Code Generation

Python allows you to generate code dynamically using techniques such as eval() or exec(). This can be useful for generating code based on certain conditions or dynamically creating functions or classes. Here's an example:

```py
name = "Karishma"
age = 2

code = f'def greet():\n    print("Name: {name}")\n    print("Age: {age}")'

exec(code)
greet()
```

Output

```py
Name: Karishma
Age: 2
```
In the above example, the code string is dynamically generated and executed using `exec()` to define the greet function.

## Most common in-built keywords and functions for metaprogramming in Python

In Python, the following keywords and concepts are commonly associated with metaprogramming and provide the foundation for metaprogramming:

1. `getattr()`, `dir()`, `hasattr()`: These functions allow you to dynamically get and set attributes of an object at runtime. The built-in `inspect` module is responsible for an important concept of metaprogramming called introspection in which the program simply looks at and reports on itself.

2. `exec()`, `eval()` and `compile()`: These functions enable the execution of dynamically generated code strings or evaluation of expressions.

3. `__getattr__()` and `__setattr__()`: These special methods can be defined in classes to handle attribute access and assignment dynamically.

4. `__metaclass__`: This special attribute can be used in a class definition to specify a metaclass for that class.

## Conclusion

These are some of the ways to achieve metaprogramming in Python. Each technique provides flexibility in manipulating code structures and behavior, enabling you to create more dynamic and flexible applications.

---
If you like what you read, consider [subscribing to my newsletter](https://karishmashukla.substack.com/).
Find me on [GitHub](https://github.com/karishmashuklaa), [Twitter](https://twitter.com/amhsirak_)