Decorators or Wrappers (@) in python are used to provide additional characteristic to a function. In other words a decorator wraps a function, modifying its behavior.
These are simply other python functions which return functions as return values.
Few common usages of decorators are
@login_required: To validate if a user is logged in, otherwise load the login page
@memoize: To cache return value of a function based upon arguments
@patch: To monkey patch a module or object members.
Implement decorator as a function:
Following could be an example of a decorator which when used at declaration of a function, calls the same with a static argument, and returns the result
As you can observe, in the current format the decorator isn’t of much use since the arguments to the decoratee function is fixed. Lets try if we can improve on that.
Lets try to write a memoized function. A simple implementation could be a
@memoize decorator which stores the map of function input and output values. The decorator only calls the function if no records are found in the map, otherwise just return the results from the map. This kind of caching is useful when optimizing a heavy function.
We can also use helper
@wraps to give the impression that we are actually calling adder by preserving its metadata
Implement decorator as a class:
Since memoize function here is a callable object, lets try to implement the same using a class
In this example
__call__ method is used to invoke the underlying function.
__call__ method is invoked when calling an instance of a class, whereas instance is created by
__init__. We are invoking
functools.update_wrapper directly to set the func metadata.
Passing arguments to a decorator:
Now lets see how we can change the behavior of a decorator based upon arguments passed to it. In this case change the persistence method to a cache file.
In case of passing arguments to decorator. Most of the heavy lifting is done by
__init__ is already invoked with arguments at the time of attaching decorator to a function.
Using the decorator with
Things to keep in mind while writing decorators:
- A proper decorator can also be called as ordinary function, without “@” signature
- Always keep in mind to return value from the wrapper and also the underlying function
- Always keep provision for
*args, **kwargsin all functions