如何增强Python对象的方法?

4 投票
4 回答
1853 浏览
提问于 2025-04-16 07:26

我有一个包含 Spam 对象的列表:

class Spam:
    def update(self):
        print('updating spam!')

其中有些可能是 SpamLite 对象:

class SpamLite(Spam):
    def update(self):
        print('this spam is lite!')
        Spam.update(self)

我想从这个列表中随便拿一个对象,然后在它的更新方法中加点东西,类似于:

def poison(spam):
    tmp = spam.update 
    def newUpdate(self):
        print 'this spam has been poisoned!'
        tmp(self)
    spam.update = newUpdate

我希望 spam.update() 现在可以打印:

this spam has been poisoned!
updating spam!

或者

this spam has been poisoned!
this spam is lite!
updating spam!

这取决于它是 SpamLite 还是普通的 Spam。

但是这样做不行,因为 spam.update() 不会自动传入 self 参数,而且如果 tmp 超出作用域或者发生变化,就不会调用旧的更新方法。有没有办法可以做到这一点呢?

4 个回答

0

在Python的世界里,猴子补丁(Monkey Patching)是不太受欢迎的做法。

你其实应该使用混入(Mixin)的方法,并且利用多重继承。

这样你就可以动态地替换(更新)父类,以达到你想要的效果。

3

另一种方法是使用 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()
4
def poison(spam):
    tmp = spam.update
    def newUpdate():
        print 'this spam has been poisoned!'
        tmp()
    spam.update = newUpdate

完整的脚本:

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)

输出:

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!

撰写回答