I've read that it is possible to add a method to an existing object (i.e., not in the class definition) in Python.
I understand that it's not always good to do so. But how might one do this?
I've read that it is possible to add a method to an existing object (i.e., not in the class definition) in Python.
I understand that it's not always good to do so. But how might one do this?
In Python, there is a difference between functions and bound methods.
>>> def foo():
... print "foo"
...
>>> class A:
... def bar( self ):
... print "bar"
...
>>> a = A()
>>> foo
<function foo at 0x00A98D70>
>>> a.bar
<bound method A.bar of <__main__.A instance at 0x00A9BC88>>
>>>
Bound methods have been "bound" (how descriptive) to an instance, and that instance will be passed as the first argument whenever the method is called.
Callables that are attributes of a class (as opposed to an instance) are still unbound, though, so you can modify the class definition whenever you want:
>>> def fooFighters( self ):
... print "fooFighters"
...
>>> A.fooFighters = fooFighters
>>> a2 = A()
>>> a2.fooFighters
<bound method A.fooFighters of <__main__.A instance at 0x00A9BEB8>>
>>> a2.fooFighters()
fooFighters
Previously defined instances are updated as well (as long as they haven't overridden the attribute themselves):
>>> a.fooFighters()
fooFighters
The problem comes when you want to attach a method to a single instance:
>>> def barFighters( self ):
... print "barFighters"
...
>>> a.barFighters = barFighters
>>> a.barFighters()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: barFighters() takes exactly 1 argument (0 given)
The function is not automatically bound when it's attached directly to an instance:
>>> a.barFighters
<function barFighters at 0x00A98EF0>
To bind it, we can use the MethodType function in the types module:
>>> import types
>>> a.barFighters = types.MethodType( barFighters, a )
>>> a.barFighters
<bound method ?.barFighters of <__main__.A instance at 0x00A9BC88>>
>>> a.barFighters()
barFighters
This time other instances of the class have not been affected:
>>> a2.barFighters()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: A instance has no attribute 'barFighters'
More information can be found by reading about descriptors and metaclass programming.
Module new is deprecated since python 2.6 and removed in 3.0, use types
see http://docs.python.org/library/new.html
In the example below I've deliberately removed return value from patch_me() function.
I think that giving return value may make one believe that patch returns a new object, which is not true - it modifies the incoming one. Probably this can facilitate a more disciplined use of monkeypatching.
import types
class A(object):#but seems to work for old style objects too
pass
def patch_me(target):
def method(target,x):
print "x=",x
print "called from", target
target.method = types.MethodType(method,target)
#add more if needed
a = A()
print a
#out: <__main__.A object at 0x2b73ac88bfd0>
patch_me(a) #patch instance
a.method(5)
#out: x= 5
#out: called from <__main__.A object at 0x2b73ac88bfd0>
patch_me(A)
A.method(6) #can patch class too
#out: x= 6
#out: called from <class '__main__.A'>
In Python monkeypatching generally works by overwriting a class or function's signature with your own. Below is an example from the Zope Wiki:
from SomeOtherProduct.SomeModule import SomeClass
def speak(self):
return "ook ook eee eee eee!"
SomeClass.speak = speak
This code will overwrite/create a method called speak in the class. In Jeff Atwood's recent post on monkey patching, he showed an example in C# 3.0 which is the current language I use for work.
What you're looking for is setattr I believe.
Use this to set an attribute on an object.
>>> def printme(s): print repr(s)
>>> class A: pass
>>> setattr(A,'printme',printme)
>>> a = A()
>>> a.printme() # s becomes the implicit 'self' variable
< __ main __ . A instance at 0xABCDEFG>
Since this question asked for non-Python versions, here's JavaScript:
a.methodname = function () { console.log("Yay, a new method!") }
What Jason Pratt posted is correct.
>>> class Test(object):
... def a(self):
... pass
...
>>> def b(self):
... pass
...
>>> Test.b = b
>>> type(b)
<type 'function'>
>>> type(Test.a)
<type 'instancemethod'>
>>> type(Test.b)
<type 'instancemethod'>
As you can see, Python doesn't consider b() any different than a(). In Python all methods are just variables that happen to be functions.
class UnderWater:
def __init__(self):
self.net = 'underwater'
marine = UnderWater() # Instantiate the class
# Recover the class from the instance and add attributes to it.
class SubMarine(marine.__class__):
def __init__(self):
super().__init__()
self.sound = 'Sonar'
print(SubMarine, SubMarine.__name__, SubMarine().net, SubMarine().sound)
# Output
# (__main__.SubMarine,'SubMarine', 'underwater', 'Sonar')
Thanks to Arturo! Your answer got me on the right track!
Based on Arturo's code, I wrote a little class:
from types import MethodType
import re
from string import ascii_letters
class DynamicAttr:
def __init__(self):
self.dict_all_files = {}
def _copy_files(self, *args, **kwargs):
print(f'copy {args[0]["filename"]} {args[0]["copy_command"]}')
def _delete_files(self, *args, **kwargs):
print(f'delete {args[0]["filename"]} {args[0]["delete_command"]}')
def _create_properties(self):
for key, item in self.dict_all_files.items():
setattr(
self,
key,
self.dict_all_files[key],
)
setattr(
self,
key + "_delete",
MethodType(
self._delete_files,
{
"filename": key,
"delete_command": f'del {item}',
},
),
)
setattr(
self,
key + "_copy",
MethodType(
self._copy_files,
{
"filename": key,
"copy_command": f'copy {item}',
},
),
)
def add_files_to_class(self, filelist: list):
for _ in filelist:
attr_key = re.sub(rf'[^{ascii_letters}]+', '_', _).strip('_')
self.dict_all_files[attr_key] = _
self._create_properties()
dy = DynamicAttr()
dy.add_files_to_class([r"C:\Windows\notepad.exe", r"C:\Windows\regedit.exe"])
dy.add_files_to_class([r"C:\Windows\HelpPane.exe", r"C:\Windows\win.ini"])
#output
print(dy.C_Windows_HelpPane_exe)
dy.C_Windows_notepad_exe_delete()
dy.C_Windows_HelpPane_exe_copy()
C:\Windows\HelpPane.exe
delete C_Windows_notepad_exe del C:\Windows\notepad.exe
copy C_Windows_HelpPane_exe copy C:\Windows\HelpPane.exe

This class allows you to add new attributes and methods at any time.
Apart from what others said, I found that __repr__ and __str__ methods can't be monkeypatched on object level, because repr() and str() use class-methods, not locally-bounded object methods:
# Instance monkeypatch
[ins] In [55]: x.__str__ = show.__get__(x)
[ins] In [56]: x
Out[56]: <__main__.X at 0x7fc207180c10>
[ins] In [57]: str(x)
Out[57]: '<__main__.X object at 0x7fc207180c10>'
[ins] In [58]: x.__str__()
Nice object!
# Class monkeypatch
[ins] In [62]: X.__str__ = lambda _: "From class"
[ins] In [63]: str(x)
Out[63]: 'From class'