Decorators in Python

Published on:
September 6, 2022

Decorators in Python are important and most powerful features. In this article, we will first discuss the prerequisites of Decorators and then we will discuss Decorators in Python with examples to understand how to use them with classes and functions. 

Decorator

A decorator is a function, adds some functionality and returns it. Meaning, it adds functionality to the existing code without modifying the existing code. Modifying another part of the program at compile time is known as meta-programming. Because of this feature (extends its behavior without explicitly modifying it), its applications are in the real-world like debugging, logging, measuring execution time, authentication, etc.

Prerequisites

We must be comfortable with the fact of how functions work, Inner functions, higher-order functions, and first-class objects. 

Functions in Python

A function takes input(s), i.e., argument(s) and return a value, i.e., output. A function may have print which is a side effect because it gives output to the console directly.

This below function is without print

Functions in Python

First-Class Object

A function in Python is treated as a first class object means it is same as any other variable in the language. So we can assign function to a variable similar to what we might be doing with the usual variables.

It is the instance of Object type, returns function from a function, stores the function in a variable or in data structures like list, dict, etc.

Example:

Function as a first class object
Function as a first class object

Here name and myName will both print same output as they refer to same function object.

Passing a function to another function

Pass a function as an argument to another function
Pass a function as an argument to another function

In this example, function name accepts a function and calls it twice in its body. There is another function my_name which is passed to function name, so “Mithlesh” will be printed twice.

Returning a function from a function

Return a function from a function
Return a function from a function

Inner/Nested functions

Inner or Nested Functions
Inner or Nested Functions

`one_child` and `another_child` are the Inner functions of `parent` function. These inner functions are locally scoped only, otherwise you will get NameError when you try to call them from outside of the scope. These inner functions can access global functions or variables.

Use of inner functions
Use of inner functions

What are Decorators?

Now let's understand what are decorators in Python. As discussed earlier, decorators are used to modify behaviour of a function without changing it.

Basic decorator structure
Basic decorator structure

Decorator calls its inner function `wrapper` before calling the `status` inside the decorator. `wrapper` function references to the original function `status` as `fun` parameter which is calling in-between the two prints. It means a decorator can modify a function by wrapping it with additional changes.

Syntactic Decorator

Given above code is not standard way to represent decorators because same function will be repeated, so there is another way to use decorators in Python.  

Syntax for decorator
Syntax for decorator

Preserving name and docstring

Losing the decorated function name and docstring
Losing the decorated function name and docstring

Check the output in above code, it has changed the function name and its docstring because of `status` is decorated. To avoid this, we should use `functools.wraps` on the wrapper function. Therefore, the updated code is

Preserve the original function name and its docstring
Preserve the original function name and its docstring

Reusing Decorator

Decorators are reusable as these are just regular functions in the Python

Function `thrice` is a simple decorator which calls the function `fun` 3 times in it.

Decorators functions with Parameters

Consider the following example where decorated function has parameters as well

This error occurs because of wrapper function does not accept any arguments, you should fix as given below

*args and **kwargs  are useful when we need to allow multiple position arguments or keyword argument to a function respectively.

Returning values from a decorated function

Lets consider a function to multiply two numbers and return result of the multiplication.

The resultant value of the multiplication is `None`, because the `wrapper` function does not return anything. So we need to return something from function to get the desired value.

Decorators with Arguments

Decorators can also have arguments. You should define decorator inside another function which has arguments and those arguments can be used inside the decorator and return from it.

Let us generalize the thrice to n times, meaning we were repeating 3 times only but now we will repeat it for n given times.

The inner most wrapper function def wrapper(*args, **kwargs): takes a variable then calls decorated function num, that how many times have to repeat and returns value given by decorated function. Other inner function def decorator_times(fun): returns the wrapper function. Outer most function def times(num): takes an argument and goes to inner functions.

Hence you can also use arguments to a decorator, only you need to wrap them inside of another decorator function.

Chaining or Nested Decorators

As name says, we chain multiple decorators to a single function. 

Order matters here, first to_upper and then split_string will be used. It can be written as split_string(to_upper(mess)) and assign to mess  or as given below

Note that we can also apply multiple decorators in a function.

Fancy Decorators

If you know Classes in Python then you can easily understand this topic easily. You have learnt decorators on functions, now you will learn decorators with classes which is also known as Fancy Decorators in Pyhon. 

These are types  Decorating methods of class and decorating complete class.

Decorating Methods of a Class

There are built-in decorators for decorating methods of class  @classmethod  bound to class but not object, shared with object. Class is first parameter to pass. @staticmethod  part of class namespace and can not modify object state or class state. @property creates getter and setter.

In this example, we have included all the three types @classmethod, @staticmethod, and @property . Class name is browser and with_incognito is a factory method which creates incognito window objects. 

Decorating a Complete Class

It is very similar to writing a function decorator. Decorator receives a class but not a function as argument which is only difference between decorating function and decorating class. When you decorate a class, then class does not decorate its methods. See its structure which is similar as decorating function as previous

className = decorator(className) 
it means add functionality to instatiation process of class.
Consider the example, decorating a Class

Hence, decorator can be used with whole or with the methods only. Note that when you use decorator used with whole class then its not add with method of the same class.

Classes as Decorators

You can use decorators with class and also you can also use classes as decorator. To store state of the data, classes can be used. In  given example, we will implement stateful decorator with a class which record states of data, here number of class made for a function.

You need _init_ and _call_ to make a class as a decorator. These take function as argument and class wil be used as decorator and it implement _call_ method. Decorator must be callable object. Instead of functools.wraps, we will functools.update_wrapper used when class as a decorator.

After decoration, _call_ method is called instead of mess method of the class, which means, by implementing _call_ method and passing function to _init_ as argument, classes works as decorator.

Real World Usage of Decorators

There are various real world uses of decorators, for in logging and debugging code, Validation JSON, Caching return values of a function and Authorization framework like Django.

We have an example code that to measure execution time of a function.

So, this is conclusion of this article that a decorator are first class objects in Python which extends behavior of code without changing its function, with the help of decorator, i.e,. @decorator. We can use decorator in function or method of a class or in whole class, and also classes can also used as decorator. Decorator has various application like logging, authentication, measuring execution time.


Similar Posts