Using types.MethodType to Bind Functions in Python

In Python, dynamically attaching a standalone function directly to a specific object instance does not automatically turn that function into a bound method. This article explains the purpose of types.MethodType, illustrating how it binds a function to an existing object so that the instance is implicitly passed as the first argument (self), and why standard attribute assignment fails to achieve this behavior.

The Problem with Direct Function Assignment

When a function is defined inside a class definition, Python's descriptor protocol automatically converts it into a bound method when accessed through an instance. However, assigning a standalone function directly to an instance after creation bypasses this mechanism.

Consider the following example:

class Car:

    def __init__(self, model):
        self.model = model


def drive(self):
    return f"{self.model} is driving."


car = Car("Sedan")
car.drive = drive
car.drive()  # Raises TypeError: drive() missing 1 required positional argument: 'self'

In this scenario, car.drive is treated merely as an instance attribute holding a reference to a regular function. When invoked as car.drive(), Python does not pass car to self, causing a TypeError.

The Purpose of types.MethodType

The types.MethodType constructor solves this problem by manually creating a bound method object at runtime. Its signature accepts two primary arguments:

types.MethodType(function, instance)

When you wrap a function and an instance with types.MethodType, Python constructs a method object identical to one created by standard class instantiation. This ensures that whenever the method is called on that instance, the instance itself is automatically injected as the first parameter.

How to Implement types.MethodType

To attach the function properly, import the types module and assign the result of MethodType to the instance attribute:

import types


class Car:

    def __init__(self, model):
        self.model = model


def drive(self):
    return f"{self.model} is driving."


car = Car("Sedan")

# Bind the function dynamically to the instance
car.drive = types.MethodType(drive, car)

# Now it functions like a standard instance method
print(car.drive())  # Output: Sedan is driving.

Instance Isolation

A key feature of using types.MethodType is that it modifies only the target instance. Other instances of the same class remain unaffected:

car_two = Car("SUV")
print(hasattr(car_two, "drive"))  # Output: False

If the goal were to add the method to all existing and future instances of Car, you would assign the function directly to the class instead (Car.drive = drive), which invokes the standard descriptor protocol. types.MethodType is specifically intended for per-instance runtime modification, commonly used in monkey patching, mock testing, dynamic mixins, and state-pattern architectures.