Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/design-patterns/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
在Python中实现decorator模式_Python_Design Patterns - Fatal编程技术网

在Python中实现decorator模式

在Python中实现decorator模式,python,design-patterns,Python,Design Patterns,我想在Python中实现,我想知道是否有一种方法可以编写一个decorator,只实现它想要修改的函数,而不为所有刚刚转发到修饰对象的函数编写模板。像这样: class foo(object): def f1(self): print "original f1" def f2(self): print "original f2" class foo_decorator(object): def __init__(self, decorat

我想在Python中实现,我想知道是否有一种方法可以编写一个decorator,只实现它想要修改的函数,而不为所有刚刚转发到修饰对象的函数编写模板。像这样:

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
的调用自动转发到
decoree.f2
。有没有一种方法可以编写一个通用方法,将所有未实现的函数调用转发给
decoreee

这可能不是最佳实践,但您可以向实例添加功能,正如我帮助将代码从Django的ORM转换为SQLAlachemy所做的那样,如下所示:

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

您可以使用
\uuu getattr\uuuu

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()

链接的维基百科文章中的UML图是错误的,您的代码也是错误的

如果遵循“decorator模式”,则decorator类是从基本装饰类派生的。(在UML图中,缺少从WindowDecorator到Window的继承箭头)

您不需要实现未修饰的方法


顺便说一句:在强类型语言中,还有一个原因,为什么装饰器必须从装饰类派生:否则您将无法链接装饰器。

作为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 Decorator(object):
    def __new__(cls, decoratee):
        cls = type('decorated',
                   (cls, decoratee.__class__),
                   decoratee.__dict__)
        return object.__new__(cls)
这可能就足够了:


补充@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()

在我的一个项目中,我还需要做一件特殊的事情,那就是即使是底层对象也应该实际执行在decorator中重新实现的方法。如果你知道目标在哪里,这其实很容易做到

用例是:

  • 我有一个带有方法A和B的对象X
  • 我创建了一个覆盖a的装饰器类Y
  • 如果我实例化Y(X)并调用A,它将按预期使用修饰的A
  • 如果B调用A,那么如果我实例化Y(X)并在decorator上调用B,那么来自B内部的调用将转到原始对象上的旧A,这是不需要的。我希望旧的B也叫新的A
有可能达到这样的行为:

导入检查
导入六个#以处理2-3兼容性
类MyBaseDecorator(对象):
定义初始(自,装饰):
自我装饰的
def _ugetattr _;(self,attr):
value=getattr(自装饰,attr)
如果检查方法(值):
函数=六。获取方法函数(值)
值=函数。\uuuu获取(self,type(self))
返回值
类SomeObject(对象):
def a(自我):
通过
def b(自我):
通过
类MyDecorator(MyBaseDecorator):
def a(自我):
通过
装饰=MyDecorator(SomeObject())
这可能不是开箱即用的,因为除了getattr方法之外,我还从头顶键入了其他所有内容

代码在修饰对象中查找请求的属性,如果它是一个方法(现在对属性不起作用,但支持属性的更改应该不会太困难),那么代码会将实际函数从方法中拉出,并使用描述符接口调用将函数“重新绑定”为方法,但是在装饰上。然后它被返回并很可能被执行

这样做的效果是,如果
b
曾经对原始对象调用
a
,那么当您对该对象进行装饰并且有来自装饰器的任何方法调用时,装饰器会确保访问的所有方法都绑定到装饰器,因此,使用decorator而不是原始对象查找对象,因此decorator中指定的方法优先


注:是的,我知道它看起来很像继承,但这是在多个对象的组合意义上完成的。

在Python3中,Philipp的公认答案提出了
运行时错误:超过了最大递归深度

我的工作方式是:

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

解决方案的灵感来自于

我也考虑过这一点,但我觉得这是非常错误的。也许是因为我来自C++,这是错误的。这可以被认为是猴子修补术。然而,它只需要很少的代码就可以很好地工作,这是一个非常不同的世界,当一切都是动态的时,它有更多的可能性&通过引用。我喜欢它。对于这个例子,其他任何东西都会带来更大的复杂性。你能举一个简单的子类化不起作用的例子吗?您也可以动态地创建子类-对于不能这样做或不支持多继承的语言,此模式似乎是一种变通方法。我想对一个对象应用不同的装饰器,并能够再次删除它们。子类化无法在创建实例后更改实例,或者可以更改吗?如果您有class
A
和change
A
,即添加一个新方法,
A.foo=lambda self:self
,这将反映在一个实例的所有实例上。。因为一切都是在运行时确定的。生成绝对不可维护代码的好方法。@THC4K:decorator模式(与python decorators相反)用于在运行时向对象添加行为。如果操作正确,这实际上是非常可维护的,这就是我发布这个问题的原因。我想用Python找到实现这一点的方法。这是一个好主意。但是,应该注意的是,如果您使用抽象基类(即导入abc并使用metaclass=abc.ABCMeta来定义抽象方法和属性),那么它将不起作用。这对str之类的函数不起作用。发生了什么
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