在Python中实现装饰器模式

28 投票
7 回答
15882 浏览
提问于 2025-04-16 00:26

我想在Python中实现一种叫做“装饰器模式”的东西。我在想有没有办法写一个装饰器,让它只实现想要修改的函数,而不需要为所有只是转发给被装饰对象的函数写很多重复的代码。比如这样:

class foo(object):
    def f1(self):
        print "original f1"
    def f2(self):
        print "original f2"

class foo_decorator(object):
    def __init__(self, decoratee):
        self._decoratee = decoratee
    def f1(self):
        print "decorated f1"
        self._decoratee.f1()
    def f2(self):              # I would like to leave that part out
        self._decoratee.f2()

我希望对foo_decorator.f2的调用能自动转发到decoratee.f2。有没有办法写一个通用的方法,把所有未实现的函数调用都转发到decoratee呢?

7 个回答

2

虽然这可能不是最好的做法,但你可以给实例添加功能,就像我为了将我的代码从Django的ORM迁移到SQLAlchemy时所做的那样,具体方法如下:

def _save(self):
    session.add(self)
    session.commit()
setattr(Base,'save',_save)
10

作为对Philipp回答的补充;如果你不仅想给一个对象加装饰,还想保持它的类型,Python允许你在运行时对一个实例进行子类化:

class foo(object):
    def f1(self):
        print "original f1"

    def f2(self):
        print "original f2"


class foo_decorator(object):
    def __new__(cls, decoratee):
        cls = type('decorated',
                   (foo_decorator, decoratee.__class__),
                   decoratee.__dict__)
        return object.__new__(cls)

    def f1(self):
        print "decorated f1"
        super(foo_decorator, self).f1()


u = foo()
v = foo_decorator(u)
v.f1()
v.f2()
print 'isinstance(v, foo) ==', isinstance(v, foo)

这比你例子中严格需要的要复杂一些,因为你已经提前知道了要装饰的类。

可能就足够了:

class foo_decorator(foo):
    def __init__(self, decoratee):
        self.__dict__.update(decoratee.__dict__)

    def f1(self):
        print "decorated f1"
        super(foo_decorator, self).f1()
33

你可以使用 __getattr__ 这个方法:

class foo(object):
    def f1(self):
        print "original f1"
    def f2(self):
        print "original f2"

class foo_decorator(object):
    def __init__(self, decoratee):
        self._decoratee = decoratee
    def f1(self):
        print "decorated f1"
        self._decoratee.f1()
    def __getattr__(self, name):
        return getattr(self._decoratee, name)

u = foo()
v = foo_decorator(u)
v.f1()
v.f2()

撰写回答