创建一个包装类在现有函数前后调用预置和后置函数?
我想创建一个类,这个类可以包裹另一个类,这样当我通过这个包裹类运行一个函数时,它会在前面和后面各运行一个函数。我希望这个包裹类可以和任何类一起使用,而不需要对它们进行修改。
比如说,如果我有这样一个类。
class Simple(object):
def one(self):
print "one"
def two(self,two):
print "two" + two
def three(self):
print "three"
我可以这样使用它...
number = Simple()
number.one()
number.two("2")
到目前为止,我写了这个包裹类...
class Wrapper(object):
def __init__(self,wrapped_class):
self.wrapped_class = wrapped_class()
def __getattr__(self,attr):
return self.wrapped_class.__getattribute__(attr)
def pre():
print "pre"
def post():
print "post"
我可以这样调用它...
number = Wrapper(Simple)
number.one()
number.two("2")
这个用法和上面的一样,只是第一行换了一下。
我想要的效果是,当通过包裹类调用一个函数时,包裹类中的前置函数会先被调用,然后是被包裹类中的目标函数,最后是后置函数。我希望能做到这一点,而不需要改变被包裹类的内容,也不改变函数的调用方式,只需要改变创建类实例的语法。例如:number = Simple() 和 number = Wrapper(Simple)。
2 个回答
3
我刚刚发现,在我最初的设计中,没有办法把参数(args)和关键字参数(kwargs)传递给被包装的类。这里是更新后的答案,可以把输入传递给被包装的函数……
class Wrapper(object):
def __init__(self,wrapped_class,*args,**kargs):
self.wrapped_class = wrapped_class(*args,**kargs)
def __getattr__(self,attr):
orig_attr = self.wrapped_class.__getattribute__(attr)
if callable(orig_attr):
def hooked(*args, **kwargs):
self.pre()
result = orig_attr(*args, **kwargs)
self.post()
return result
return hooked
else:
return orig_attr
def pre(self):
print ">> pre"
def post(self):
print "<< post"
31
你快到了,只需要在 __getattr__
里面做一些自我检查,当原来的属性是可以调用的函数时,返回一个新的包装函数:
class Wrapper(object):
def __init__(self,wrapped_class):
self.wrapped_class = wrapped_class()
def __getattr__(self,attr):
orig_attr = self.wrapped_class.__getattribute__(attr)
if callable(orig_attr):
def hooked(*args, **kwargs):
self.pre()
result = orig_attr(*args, **kwargs)
# prevent wrapped_class from becoming unwrapped
if result == self.wrapped_class:
return self
self.post()
return result
return hooked
else:
return orig_attr
def pre(self):
print ">> pre"
def post(self):
print "<< post"
现在用这段代码:
number = Wrapper(Simple)
print "\nCalling wrapped 'one':"
number.one()
print "\nCalling wrapped 'two':"
number.two("2")
结果是:
Calling wrapped 'one':
>> pre
one
<< post
Calling wrapped 'two':
>> pre
two2
<< post