How can I augment the method of a Python object?
I have a list of Spam objec开发者_运维百科ts:
class Spam:
def update(self):
print('updating spam!')
some of them might be SpamLite objects:
class SpamLite(Spam):
def update(self):
print('this spam is lite!')
Spam.update(self)
I would like to be able to take an arbitrary object from the list, and add something to it's update method, something like:
def poison(spam):
tmp = spam.update
def newUpdate(self):
print 'this spam has been poisoned!'
tmp(self)
spam.update = newUpdate
I want spam.update() to now either print:
this spam has been poisoned!
updating spam!
or
this spam has been poisoned!
this spam is lite!
updating spam!
depending on whether it was a SpamLite or just a Spam.
But that doesn't work, because spam.update() won't pass in the self argument automatically, and because if tmp leaves scope or changes then it won't call the old update. Is there a way I can do this?
def poison(spam):
tmp = spam.update
def newUpdate():
print 'this spam has been poisoned!'
tmp()
spam.update = newUpdate
Full Script:
class Spam:
def update(self):
print('updating spam!')
class SpamLite(Spam):
def update(self):
print('this spam is lite!')
Spam.update(self)
def poison(spam):
tmp = spam.update # it is a bound method that doesn't take any arguments
def newUpdate():
print 'this spam has been poisoned!'
tmp()
spam.update = newUpdate
from operator import methodcaller
L = [Spam(), SpamLite()]
map(methodcaller('update'), L)
map(poison, L)
print "*"*79
map(methodcaller('update'), L)
Output:
updating spam! this spam is lite! updating spam! ******************************************************************************* this spam has been poisoned! updating spam! this spam has been poisoned! this spam is lite! updating spam!
Another approach, with MethodType:
class Spam:
def update(self):
print('updating spam!')
class SpamLite(Spam):
def update(self):
print('this spam is lite!')
Spam.update(self)
def poison(spam):
import types
tmp = spam.update
def newUpdate(self):
print 'this spam has been poisoned!'
tmp()
newUpdate = types.MethodType(newUpdate, spam, Spam)
spam.update = newUpdate
spam = Spam()
spam_lite = SpamLite()
poison(spam)
poison(spam_lite)
spam.update()
print
spam_lite.update()
MonkeyPatching is frowned upon, in the python world.
You should really use the Mixin approach and use Multiple inheritance.
You can then dynamically replace (update) the parents to achieve the desired effect.
Use decorators like this:
def method_decorator(f):
def wrapper(self, *args, **kwargs):
print('this spam has been poisoned!')
return f(self)
return wrapper
class Spam:
def update(self):
print('updating spam!')
@method_decorator
def update2(self):
print('updating spam!')
Spam().update()
Spam().update2()
This prints:
updating spam!
this spam has been poisoned!
updating spam!
If you want to know more about decorators read this: http://www.drdobbs.com/web-development/184406073
The above is not a "good citizen" decorator, read the article to know how to write one. Be sure to check also decorator library: http://pypi.python.org/pypi/decorator
HTH
精彩评论