创建一个包装类在现有函数前后调用预置和后置函数?

16 投票
2 回答
31550 浏览
提问于 2025-04-15 14:33

我想创建一个类,这个类可以包裹另一个类,这样当我通过这个包裹类运行一个函数时,它会在前面和后面各运行一个函数。我希望这个包裹类可以和任何类一起使用,而不需要对它们进行修改。

比如说,如果我有这样一个类。

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

撰写回答