我想在Python中实现decorator pattern,我想知道是否有办法编写一个装饰器来实现它想要修改的函数,而不需要为所有函数编写样板转发到装饰对象。像这样:
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
?
答案 0 :(得分:32)
您可以使用__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()
答案 1 :(得分:9)
作为菲利普回答的附录;如果您不仅需要修饰,而且还要保留对象的类型,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()
答案 2 :(得分:2)
这可能不是最好的做法,但你可以为实例添加功能,就像我帮助将代码从Django的ORM转换到SQLAlachemy一样,如下所示:
def _save(self):
session.add(self)
session.commit()
setattr(Base,'save',_save)
答案 3 :(得分:2)
链接的维基百科文章中的UML图是错误的,您的代码也是错误的。
如果您遵循“装饰器模式”,则装饰器类派生自基础装饰类。 (在UML图中,缺少从WindowDecorator到Window的继承箭头。)
与
class foo_decorator(foo):
您不需要实施未修饰的方法。
BTW:在强类型语言中还有另外一个原因,为什么装饰器必须从装饰类派生:否则你将无法链接装饰器。答案 4 :(得分:0)
补充@Alec Thomas回复。我修改了他的答案以遵循装饰模式。这样你就不需要提前知道你正在装修的课程。
class Decorator(object):
def __new__(cls, decoratee):
cls = type('decorated',
(cls, decoratee.__class__),
decoratee.__dict__)
return object.__new__(cls)
然后,您可以将其用作:
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()
答案 5 :(得分:0)
在我的一个项目中,我还需要做一件特别的事情,即即使是底层对象也应该实际执行在装饰器中重新实现的方法。如果你知道在哪里定位,它实际上很容易做到。
用例是:
可以像这样达到这种行为:
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方法之外的所有其他内容。
代码在装饰对象中查找请求的属性,如果它是一个方法(现在不对属性起作用,但是支持它们的更改不应该太困难),代码然后拉出方法中的实际函数和使用描述符接口调用它" rebinds"作为方法的函数,但在装饰器上。然后它被返回并且很可能被执行。
这样做的结果是,如果b
曾在原始对象上调用a
,那么当您装饰对象并且装饰器中有任何方法调用时,装饰器会确保访问的所有方法都绑定到装饰器,因此使用装饰器而不是原始对象查找事物,因此装饰器中指定的方法优先。
P.S。:是的我知道它看起来非常像继承,但这是在多个对象组合的意义上完成的。
答案 6 :(得分:0)
在Python 3中,Philipp接受的答案提出了RuntimeError: maximum recursion depth exceeded
。
对我有效的方式:
class Foo(object):
def f1(self):
print("original f1")
def f2(self):
print("original f2")
class FooDecorator(object):
def __init__(self, decoratee):
self._decoratee = decoratee
def f1(self):
print("decorated f1")
return self._decoratee.f1()
def __getattr__(self, name):
if name in ['f1', '_decoratee']:
raise AttributeError()
return getattr(self._decoratee, name)
f = FooDecorator(Foo())
f.f1()
# decorated f1
# original f1
f.f2()
# original f2
此变通办法的灵感来自Ned Batchelder's blog