您提到您希望在正常操作期间拦截方法调用,而不仅仅是在调试期间。这个解决方案是一种与其他答案不同的方法,它使用代理模式方法来创建一个新对象,该对象将成为对原始对象的所有调用的一种包装器。在代理级别,您可以根据需要记录和监控呼叫。
人.py
为了演示代码,我创建了一个Person
在person
模块中调用的示例类。我们可以假设这是您无法修改代码的模块。
class Person(object):
def __init__(self, name=None):
self.name = name
def greet(self, greeting='hello'):
print '%s %s' % (greeting, self.name)
def __repr__(self):
return "Person(%r)" % self.name
例子.py
在下面的代码中,我们创建了一个名为的代理对象ProxyPerson
,当使用它时,它的Person
行为与其相同,并且具有记录所有对 greet 方法的调用的附加行为。您注意到它使用*args, **kwargs
了因此 Person 将来可能具有不同的签名,但如果进行更改,代码不会中断。
import person
class ProxyPerson(person.Person):
def greet(self, *args, **kwargs):
print '--- greet() self=%r args=%r kwargs=%r' % (self, args, kwargs)
super(ProxyPerson, self).greet(*args, **kwargs)
#person.Person = ProxyPerson #uncomment to monkey patch Person
jack, jill = person.Person('Jack'), ProxyPerson('Jill')
for p in jack, jill:
p.greet()
p.greet('hi')
p.greet(greeting='why hello')
常规输出
下面的输出显示了使用原始 Person 类或使用 ProxyPerson 时的执行差异。这些示例还包括不带位置参数、带位置参数和最后带关键字参数的调用。
hello Jack
hi Jack
why hello Jack
--- greet() self=Person('Jill') args=() kwargs={}
hello Jill
--- greet() self=Person('Jill') args=('hi',) kwargs={}
hi Jill
--- greet() self=Person('Jill') args=() kwargs={'greeting': 'why hello'}
why hello Jill
猴子补丁输出
最后一个可以将Monkey 补丁 person.Person
类指向 ProxyPerson。您可以通过在example.py
. 这种情况下的输出如下:
--- greet() self=Person('Jack') args=() kwargs={}
hello Jack
--- greet() self=Person('Jack') args=('hi',) kwargs={}
hi Jack
--- greet() self=Person('Jack') args=() kwargs={'greeting': 'why hello'}
why hello Jack
--- greet() self=Person('Jill') args=() kwargs={}
hello Jill
--- greet() self=Person('Jill') args=('hi',) kwargs={}
hi Jill
--- greet() self=Person('Jill') args=() kwargs={'greeting': 'why hello'}
why hello Jill
猴子补丁的好处是所有未来的 person.Person 实例实际上都是已创建的代理对象的实例。Monkey patching 有它自己的一系列问题,在使用它之前你应该记住这些问题。