Implementing the decorator pattern in Python

后端 未结 7 1543
梦谈多话
梦谈多话 2020-12-02 09:59

I want to implement the decorator pattern in Python, and I wondered if there is a way to write a decorator that just implements the function it wants to modify, without writ

相关标签:
7条回答
  • 2020-12-02 10:32

    In Python 3, Philipp's accepted answer raised RuntimeError: maximum recursion depth exceeded.

    The way that worked for me:

    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
    

    The workaround is inspired by Ned Batchelder's blog

    0 讨论(0)
  • 2020-12-02 10:39

    You could use __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()
    
    0 讨论(0)
  • 2020-12-02 10:42

    The UML diagram in the linked Wikipedia article is wrong and so is your code.

    If you follow the "decorator pattern", the decorator class is derived from the base decorated class. (In the UML diagram an inheritance arrow from the WindowDecorator to Window is missing).

    with

    class foo_decorator(foo):
    

    you don't need to implement undecorated methods.

    BTW: In strong typed languages there is one more reason, why the decorator must be derived from the decorated class: Otherwise you wouldnt be able to chain decorators.

    0 讨论(0)
  • 2020-12-02 10:45

    As an addendum to Philipp's answer; if you need to not only decorate, but preserve the type of an object, Python allows you to subclass an instance at runtime:

    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)
    

    This is a bit more involved than strictly necessary for your example, where you know the class being decorated in advance.

    This might suffice:

    class foo_decorator(foo):
        def __init__(self, decoratee):
            self.__dict__.update(decoratee.__dict__)
    
        def f1(self):
            print "decorated f1"
            super(foo_decorator, self).f1()
    
    0 讨论(0)
  • 2020-12-02 10:45

    It's arguably not the best practice, but you can add functionality to instances, as I've done to help transition my code from Django's ORM to SQLAlachemy, as follows:

    def _save(self):
        session.add(self)
        session.commit()
    setattr(Base,'save',_save)
    
    0 讨论(0)
  • 2020-12-02 10:51

    To complement @Alec Thomas reply. I modified his answer to follow the decorator pattern. This way you don't need to know the class you're decorating in advance.

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

    Then, you can use it as:

    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()
    
    0 讨论(0)
提交回复
热议问题