Implementing the decorator pattern in Python
我想在python中实现decorator模式,我想知道是否有一种方法可以编写一个只实现它想要修改的函数的decorator,而不需要为所有转发到被修饰对象的函数编写Boiler Plate。就像这样:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 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() |
我想把对
您可以使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | 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() |
号
作为Philipp答案的补充;如果您不仅需要修饰,而且需要保留对象的类型,python允许您在运行时对实例进行子类化:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | 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) |
。
对于您的示例来说,这比严格要求要复杂一些,因为您知道该类是提前装饰的。
这可能就足够了:
1 2 3 4 5 6 7 | class foo_decorator(foo): def __init__(self, decoratee): self.__dict__.update(decoratee.__dict__) def f1(self): print"decorated f1" super(foo_decorator, self).f1() |
链接的维基百科文章中的UML图是错误的,您的代码也是错误的。
如果遵循"decorator模式",则decorator类是从基本decorated类派生的。(在UML图中,缺少从WindowDecorator到Window的继承箭头)。
具有
1 | class foo_decorator(foo): |
您不需要实现未修饰的方法。
顺便说一句:在强类型语言中,还有一个原因,就是为什么修饰器必须从修饰类派生:否则您将无法链接修饰器。
可以说,这不是最佳实践,但您可以向实例添加功能,正如我所做的,以帮助将代码从Django的ORM转换为sqalachemy,如下所示:
1 2 3 4 | def _save(self): session.add(self) session.commit() setattr(Base,'save',_save) |
在我的一个项目中,我还需要做一件特别的事情,即即使底层对象也应该实际执行在decorator中重新实现的方法。如果你知道目标在哪里,这实际上是很容易做到的。
用例是:
- 我有一个带有方法A和B的对象X。
- 我创建了一个覆盖a的decorator类y。
- 如果我实例化y(x)并调用a,它将按预期使用修饰a。
- 如果b调用a,那么如果我实例化y(x)并在decorator上调用b,那么从b内部调用将转到原始对象上的旧a,这是不需要的。我也希望老B给新A打电话。
小精灵
这样的行为是可能的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | import inspect import six # for handling 2-3 compatibility class MyBaseDecorator(object): def __init__(self, decorated): self.decorated = decorated def __getattr__(self, attr): value = getattr(self.decorated, attr) if inspect.ismethod(value): function = six.get_method_function(value) value = function.__get__(self, type(self)) return value class SomeObject(object): def a(self): pass def b(self): pass class MyDecorator(MyBaseDecorator): def a(self): pass decorated = MyDecorator(SomeObject()) |
当我输入除getattr方法之外的其他所有东西时,这个方法可能不适用。
代码在修饰对象中查找请求的属性,如果它是一个方法(现在对属性不起作用,但是为支持它们所做的更改不应该太困难),那么代码将实际函数从方法中提取出来,并使用描述符接口调用将函数"重新绑定"为方法,但在修饰器上。然后返回并很可能执行。
这样做的效果是,如果
P.S.:是的,我知道它看起来很像继承,但这是在多个对象的组合意义上完成的。
补充@alec thomas回复。我修改了他的答案,以遵循装饰师的模式。这样你就不需要提前知道你要装饰的班级了。
1 2 3 4 5 6 | class Decorator(object): def __new__(cls, decoratee): cls = type('decorated', (cls, decoratee.__class__), decoratee.__dict__) return object.__new__(cls) |
。
然后,您可以将其用作:
1 2 3 4 5 6 7 8 9 10 11 12 | class SpecificDecorator(Decorator): def f1(self): print"decorated f1" super(foo_decorator, self).f1() class Decorated(object): def f1(self): print"original f1" d = SpecificDecorator(Decorated()) d.f1() |
。