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: FalseIf 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.