Decorator Design Pattern in Python
What is it?
The Decorator Design Pattern gives us a method to add new functionality to an object, dynamically. Python makes implementing this pattern relatively easier as compared to other languages, due to its builtin feature called decorators. It is a Structural Design Pattern as it eases the application design by identifying a simple way to add features to an existing object dynamically.
Why the need for it: Problem Statement
If you to add new features to an existing object without having to modify it, then you can opt for the Decorator Pattern.
Terminology
-
- Decorator function: The decorator function takes an object, manipulates it using its own object and returns this latter object. For example, in the event of decorating a function:
def decoratorFunction(inputFunction): def manipulateInputFunction(): capture return value of inputFunction return manipulatedReturnValueOfInputFunction return manipulateInputFunction @decoratorFunction def functionToBeDecorated(): # body of function returns an object, say a string SIGNIFICANCE OF @ NOTATION Any call to functionToBeDecorated() BECOMES call to decoratorFunction() with functionToBeDecorated as its argument i.e. functionToBeDecorated() BECOMES decoratorFunction(functionToBeDecorated)() For example: stringOne = functionToBeDecorated() BECOMES stringOne = decoratorFunction(functionToBeDecorated)()
Pseudo Code
Following is an example of pseudo code that decorates a function. The decorator function decorateMyFunction() takes a function as input and its return value is also a function. Before beginning, it is important to understand that functions, like everything else in Python, is an object.
def decorateMyFunction(originalFunction): '''Decorates a function by wrapping its return value in a pair of HTML paragraph tags.''' def addAdditionalText(): obtain string returned by original function add text to the string return new string return addAdditionalText @decorateMyFunction def functionToBeDecorated(): '''A simple function that returns a string.''' # TESTING THE CODE print( functionToBeDecorated() )
How to implement it
Let's make the output of a function fancier by wrapping its return value with additional text. We will make use of the decorator annotation (@) provided by Python.
def decorateMyFunction(originalFunction): '''Decorates a function by wrapping its return value in a pair of HTML paragraph tags.''' def addAdditionalText(): # Obtain string returned by original function textFromOriginalFunction = originalFunction() # Adding new functionality to the function being decorated return "<p>" + textFromOriginalFunction + "</p>" return addAdditionalText @decorateMyFunction def functionToBeDecorated(): '''A simple function that returns a string.''' return "Hi there!" print( functionToBeDecorated() ) # OUTPUT: <p>Hi there!</p>
Walkthrough of implementation
- The function declared after the @decorateMyFunction gets passed to the decorateMyFunction as argument.
- So, a call to functionToBeDecorated() becomes a call to decorateMyFunction(functionToBeDecorated).
- The decorateMyFunction() method returns the function called addAdditionalText. So, the addAdditionalText() function is called.
- The addAdditionalText() function captures the return value of the function supplied to it i.e. functionToBeDecorated(), and adds additional text to it.
- This manipulated string is returned and is eventually printed.
Related to: Adapter, Composite & Strategy
- Creational Patterns
- Factory
- Abstract Factory
- Prototype
- Singleton
- Builder
- Architectural Pattern
- Model View Controller (MVC)