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 __name__, __doc__, and __module__

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 __call__, since __init__ is already invoked with arguments at the time of attaching decorator to a function.

Using the decorator with persist=True

With persist=False 

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, **kwargs in all functions

 


Naved

An adept programmer with experience in design, development and deployment of multiple application routines. Knowledge of various software designs paradigms, development frameworks, deployment methodologies, database modelling and process migration. Special focus on business driven design, search optimization, user interface, data access and data storage implementation. A hacker with a penchant for making technological processes more readily and comprehensively accessible.