Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the difference between Python decorators and the decorator pattern?

What is the difference between “Python decorators” and the “decorator pattern”?

When should I use Python decorators, and when should I use the decorator pattern?

I'm looking for examples of Python decorators and the decorator pattern accomplishing same.

@AcceptedAnswer

I know that Jakob Bowyer's answer is valid. Yet it's Srikar's answer that made me understand why.

After Srikar's answer, and studying the given resources, I've written this example, so I can visualize and understand Python decorators and the decorator pattern.

I must disagree with Srikar's "Python decorators are not an implementation of the decorator pattern". After what I've learned, I'm strongly convinced that Python decorators are an implementation of the decorator pattern. Just not in the classic way.

Also, I need to add that, despite the fact that Srikar said "Python decorators add functionality to functions and methods at definition time", you can easily use Python decorators at run time.

Yet, I still mark Srikar's answer as accepted, because it helped me understand the implementation of the decorator pattern in Python.

"""
Testing Python decorators against the decorator pattern
"""
def function(string):
    return string

def decorator(wrapped):
    def wrap(string):
        # Assume that this is something useful
        return wrapped(string.upper())
    return wrap

def method_decorator(wrapped):
    def wrap(instance, string):
        # Assume that this is something useful
        return wrapped(instance, string.upper())
    return wrap

@decorator
def decorated_function(string):
    print('! '.join(string.split(' ')))

class Class(object):
    def __init__(self):
        pass
    def something_useful(self, string):
        return string

class Decorator(object):
    def __init__(self, wrapped):
        self.wrapped = wrapped
    def something_useful(self, string):
        string = '! '.join(string.split(' '))
        return self.wrapped().something_useful(string)

    @method_decorator
    def decorated_and_useful(self,string):
        return self.something_useful(string)


if __name__ == '__main__':
    string = 'Lorem ipsum dolor sit amet.'
    print(function(string))                  # Plain function
    print(decorator(function)(string))       # Python decorator at run time
    print(decorated_function(string))        # Python decorator at definition time
    a = Class()
    print(a.something_useful(string))        # Plain method
    b = Decorator(Class)
    print(b.something_useful(string))        # Decorator pattern
    print(b.decorated_and_useful(string))    # Python decorator decorated the decorator pattern
like image 362
seler Avatar asked Nov 30 '11 15:11

seler


People also ask

Is Python decorator a decorator pattern?

Decorator is a structural pattern that allows adding new behaviors to objects dynamically by placing them inside special wrapper objects, called decorators. Using decorators you can wrap objects countless number of times since both target objects and decorators follow the same interface.

What is the main difference between the composite and the decorator patterns?

Composite is intended to combine and represent multiple objects as a single one (of the same base type) - i.e. 1 to Many, while Decorator enhances (or adds on top of) the capability of a single object of the same type - i.e. 1 to 1.

What is the Python decorator?

A decorator in Python is a function that takes another function as its argument, and returns yet another function. Decorators can be extremely useful as they allow the extension of an existing function, without any modification to the original function source code.

What is the decorator pattern also known as?

A Decorator Pattern says that just "attach a flexible additional responsibilities to an object dynamically". In other words, The Decorator Pattern uses composition instead of inheritance to extend the functionality of an object at runtime. The Decorator Pattern is also known as Wrapper.


2 Answers

Decorator Pattern - In object-oriented programming, the decorator pattern is a design pattern that allows behaviour to be added to an existing object dynamically. 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. The decorator pattern is a design pattern used in statically typed object-oriented programming languages to allow functionality to be added to objects at run time; Python decorators add functionality to functions and methods at definition time, and thus are a higher-level construct than decorator-pattern classes.

The decorator pattern itself is trivially implementable in Python, because the language is duck typed, and so is not usually considered as such. So in Python a decorator is any callable Python object that is used to modify a function, method or class definition.

I hope I made the difference clear. Just in case you did not completely understand, please go through these links. You will come out more than clear at the end of it -

  • How to make a chain of function decorators?

  • Implementing the decorator pattern in Python

  • What is the difference between using decorators and extending a sub class by inheritance?

  • Python Class Decorator

  • PyWiki - Python Decorators - A detailed discourse

  • Python Decorators Made Easy

  • Source 1 & source 2

like image 129
Srikar Appalaraju Avatar answered Oct 20 '22 15:10

Srikar Appalaraju


The difference is this:

(a) Python decorators are tied to an existing method and change the behavior of that method. Example:

@modifyBehavior
def original(myString):
    print myString

The behavior of original is overwritten. You can't use this to add a new functionality.

(b) Decorator pattern is about polymorphism. In your sample code above, the behavior of Decorator.something_useful is overwritten. The original method is lost. It's not really decorator pattern. You should be looking to enhance or add functionality, not replace a method. You should ensure that a.something_useful(string) returns the same thing as b.something_useful(string). In fact, in decorator pattern you would typically replace the original object. Here is what I mean:

class Class(object):
    def __init__(self):
        pass
    def something_useful(self, string):
        return string

class Decorator(object):
    def __init__(self, wrapped):
        self._wrapped = wrapped
    def withUnderscores(self, string):
        return '_'.join(string.split(' '))
    def __getattr__(self, name):
        return getattr(self._wrapped, name)


if __name__ == '__main__':
    string = 'Lorem ipsum dolor sit amet.'
    obj = Class()
    print('Original: ', obj.something_useful(string))
    #This has no underscore function.  Use decorator to add.
    obj = Decorator(obj)
    print('Replaced spaces: ', obj.withUnderscores(string))
    print('Original still works: ', obj.something_useful(string))

You can have several decorators to add functionality. This allows you to add only what you need when you need it. More reading: GoF

like image 21
rfportilla Avatar answered Oct 20 '22 15:10

rfportilla