Dynamically Add a Method to a Class in Python
I often use ipython
on the command line to create minimal examples of things I want to try. When I need to make things related to a class, I often find myself making a class, using it for a while, then when I need to add more methods to the class or edit a line, I have to hit up-arrow until I get back to the line with the class declaration, then resubmit the entire class to the interpreter, when all I wanted to do was add method foo
to class A
.
So I thought to myself, how can I make the following code work?
class A:
pass
a = A()def foo():
print('hello world!')### PYTHON MAGIC GOES HERE ###a.foo() # hello world!
After perusing such stack overflow posts such as https://stackoverflow.com/questions/17929543/how-can-i-dynamically-create-class-methods-for-a-class-in-python, I stumbled upon the builtinsetattr
(https://docs.python.org/3/library/functions.html#setattr) which accomplishes the task.
class A:
pass
a = A()def foo(self): # Have to add self since this will become a method
print('hello world!')setattr(A, 'foo', foo)a.foo() # hello world!
Indeed, the above does work. However, I found this solution rather inelegant.
This use case begs for some reusable bit of code, such that I can add any function to any existing class. What I want is to decorate any function so that it becomes a method to the class. I want the following code to work.
class A:
pass
a = A()@add_method(A)
def foo():
print('hello world!')a.foo() # hello world!
I want the burden of the work to be done by the decorator and not force the programmer to have to add self
to functions (which would render the functions unusable until bound to a class), and I want to simplify the use of setattr
which always requires a name and object to be bound which can be a pain for refactoring and redundant since the function itself knows its __name__
.
After following this line of thought down the rabbit hole for about an hour, here is the result.
You can copy paste this into a file and try it out for yourself.
The add_method
decorator does all the heavy lifting. Both functions foo
and bar
are still valid functions after they are bound as methods.
I’m not sure how useful this will ever be (probably not useful at all). This is just the kind of thing that I like to experiment in with ipython
.