Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Implementing the decorator pattern in Python

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 writing boiler-plate for all the functions that are just forwarded to the decorated object. Like so:

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

I would like to have calls to foo_decorator.f2 forwarded to decoratee.f2 automatically. Is there a way to write a generic method that forwards all unimplemented function-calls to decoratee?

like image 969
Björn Pollex Avatar asked Jun 25 '10 14:06

Björn Pollex


People also ask

Are Python decorators implementation of decorator pattern?

The decorator pattern can be used to extend (decorate) the functionality of a certain object at run-time, independently of other instances of the same class, provided some groundwork is done at design time. Decorators in Python - Despite the name, Python decorators are not an implementation of the decorator pattern.

What is decorator pattern Python?

The decorator pattern is a software design pattern that allows us to dynamically add functionality to classes without creating subclasses and affecting the behavior of other objects of the same class.

What is decorator in Python with example?

A decorator is a design pattern in Python that allows a user to add new functionality to an existing object without modifying its structure. Decorators are usually called before the definition of a function you want to decorate.


3 Answers

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()
like image 188
Philipp Avatar answered Oct 16 '22 10:10

Philipp


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()
like image 25
Alec Thomas Avatar answered Oct 16 '22 11:10

Alec Thomas


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)
like image 2
andyortlieb Avatar answered Oct 16 '22 11:10

andyortlieb