What is monkey patching in Python?

What is Monkey Patching in Python?

Monkey patching in Python involves the dynamic alteration of existing classes, methods, functions, and modules during runtime. Python enables these runtime modifications as an interpreted language, which is considered one of its remarkable features. The topic of monkey patching frequently arises in interviews, and I have also encountered it.

In simple work, I can say Monkey patching in Python is like using a special code to change how some existing code works while running.

Why do we need monkey patching in Python?

As we know, Python stands out as a dynamic language, allowing us to alter the functionality extensively, or more precisely, the behavior of pre-existing code during runtime.

Let’s delve into real-life instances to grasp the Python monkey patching concept. This will provide you with a clearer understanding of this concept. Imagine you’re engaged in a practical Python project. You may encounter issues with a module, class, or function not performing as intended. In such scenarios, you can adjust the behavior of said module, class, or function using a fascinating technique known as monkey patching.

Let’s walk through a concrete example to elucidate the concept of Python monkey patching.

Example:

I’ve defined two functions in the following code snippet: “addition” and “multiplication.” The “addition” function takes two inputs, a and b, and performs arithmetic addition. Similarly, the “multiplication” function takes two parameters and performs arithmetic multiplication.

Now, let’s use these functions with some values.

def addition(a, b):
    print(f"Addition of {a} and {b} is:- ", x + y)
def multiplication(a, b):
    print(f"Multiplication of {a} and {b} is:- ", x * y)
addition(5, 15)
multiplication(10, 20)
Output: 
Addition of 5 and 15 is:- 20
Multiplication of 10 and 20 is:- 200

If you see the output mentioned above, it indicates that your code is running smoothly. However, we haven’t applied the Monkey Patching concept so far. I’d like to alter how the “addition” function works. Instead of adding numbers, I want it to multiply them. We can achieve this using the Monkey Patching technique. Let’s give it a try.

Example:

In the code snippet below, I demonstrate how to modify the behavior of the ‘addition’ function. After making the change, the ‘addition’ function with specific values will perform multiplication instead of addition.

def addition(a, b):
    print(f"Addition of {a} and {b} is:- ", x + y)
def multiplication(a, b):
    print(f"Multiplication of {a} and {b} is:- ", x * y)
addition(5, 15)
# Modify the behavior of the addition function
addition = multiplication
addition(10, 20)
Output:
Addition of 5 and 15 is:- 20
Multiplication of 10 and 20 is:- 200

Explanation:

def addition(a, b): and def multiplication(a, b): These lines define two functions, addition and multiplication, which take two arguments, a and b. The addition function is intended to add the values of a and b, while the multiplication function is meant to multiply them. addition(5, 15): This line calls the addition function with the arguments 5 and 15. However, the function has a problem since it attempts to print the sum of x and y, which are not defined in the function. It should be using the arguments a and b instead. addition = multiplication: This line reassigns the name addition to refer to the multiplication function. In other words, it changes the behavior of the addition function to be the same as the multiplication function. addition(10, 20): After reassigning addition, this line calls the addition function with the arguments 10 and 20. Since addition now points to the multiplication function, it will perform multiplication, not addition.

You can dynamically change the behavior of Python functions while running the program. Let’s explore another aspect where we manipulate Python modules’ functionality using the monkey patching concept. Note: In Python, a module is a file that includes definitions and statements. A Python module can define classes, functions, and variables and may contain executable code. A collection of Python modules is called a package, and every Python package must include an `__init__.py` file.

Implement monkey patching on a Python module

We have a Python file named `student.py` containing Python code. Inside this module, there’s a class called `Student`, which includes an instance method called `full_name`. This method takes the `first_name` and `last_name` of a Student class object as parameters and displays their full name.

Now, our requirement is to modify the behavior of the `full_name` method. Instead of only displaying the full name of the student, we need it to display the first name, last name, and the full name separately. We will change this in another Python module, `main.py`, by importing the `Student` class. Let’s explore a quick example demonstrating the behavior with and without changing the class’s functionality.

Example:- Without changing the behavior of Original Class

student.py

class Student:
    def __init__(self, first_name, last_name):
        self.first_name = first_name
        self.last_name = last_name
    def full_name(self):
        print(f"Student's full name will be:- {self.first_name} {self.last_name}")

main.py

# importing Student class from student.py
from student import Student
# creating object of Student class
std1 = Student("Accu", "Cloud")
std1.full_name()

Now, execute the main.py, and you can see the following output.

Output:
Student's full name will be:- Accu Cloud

In the previous output, we obtained only the full name of the `std1` student object, which is not the desired outcome. To achieve the expected output, we must perform monkey patching.

Now, it’s time to modify the behavior of the `full_name` method within the `Student` class. To achieve this, we will define a function that alters the functionality of the `full_name` method within the `stucent.py` file. The code in `student.py` will remain unchanged, and we will make the necessary adjustments in the `main.py` module.

Example:- Changing the behavior of the Module Class

student.py

class Student:
def __init__(self, first_name, last_name):
self.first_name = first_name
self.last_name = last_name
def full_name(self):
print(f"Student's full name is:- {self.first_name} {self.last_name}")

main.py

From student import Student

def student_full_name(self):
print(f"Student's first name is:- {self.first_name}")
print(f"Student's last name is:- {self.last_name}")
print(f"Student's full name is:- {self.first_name} {self.last_name}")

# apply monkey patching concept

Student.full_name = student_full_name
std1 = Student("Accu", "Cloud")
std1.full_name()

Now, execute the main.py and you can see the following output.

Output:
Student's first name will be:- Accu
Student's last name will be:- Cloud
Student's full name will be:- Accu Cloud

Conclusion

Monkey patching is a powerful technique in Python that allows developers to modify or extend the behavior of existing classes or functions during runtime. It can be useful in certain situations, but it should be used with caution and in specific scenarios.