代码之家  ›  专栏  ›  技术社区  ›  Björn Pollex

用Python实现decorator模式

  •  24
  • Björn Pollex  · 技术社区  · 14 年前

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

    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

    6 回复  |  直到 14 年前
        1
  •  32
  •   Philipp    14 年前

    __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()
    
        2
  •  9
  •   Alec Thomas    14 年前

    作为菲利普回答的附录;如果你不仅要装饰,还要保存 类型

    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()
    
        3
  •  2
  •   andyortlieb    14 年前

    def _save(self):
        session.add(self)
        session.commit()
    setattr(Base,'save',_save)
    
        4
  •  2
  •   IanH    14 年前

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

    如果遵循“decorator模式”,则decorator类派生自修饰的基类(在UML图中,从WindowDecorator到Window的继承箭头丢失了)。

    具有

    class foo_decorator(foo):
    

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

        5
  •  0
  •   synack    9 年前

    补充@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()
    
        6
  •  0
  •   Milan FaleÅ¡ník    7 年前

    用例是:

    • 我创建了一个decorator类Y来重写a。
    • 如果B调用A,那么如果我实例化Y(X)并在decorator上调用B,那么来自B内部的调用将转到原始对象上的旧A,这是不需要的。我要旧的B也叫新的A。

    有可能达到这样的行为:

    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方法之外,我还从头顶键入了其他所有内容。

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

    其结果是如果 b 打过电话吗 a

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

        7
  •  0
  •   David Taub    5 年前

    在Python3中,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