DEV Community

Cover image for Python Decorators
Mbuthi Mungai
Mbuthi Mungai

Posted on • Edited on

Python Decorators

Python is an interpreted, object-oriented, high-level programming language.

Decorators are function that allows users to add new functionality to an existing object without changing it's structure.

Prerequisites

  1. Basic understanding of python functions.
  2. **args and **kwargs

Python functions are defined as first class objects. This means that the python functions can be assigned to variables, pass them as arguments to other functions, nested in other functions, and return them as values in other functions.

To well grasp python decorators we will dive in first to this concept of functions as first class objects.

1. Assigning functions to variables

Here we will demonstrate this concept by creating a function that greets a user with a "Hello World" message.

def greet():
    return "Hello World"
message = greet
print(message())
Enter fullscreen mode Exit fullscreen mode

The output of the above code is Hello World

2. Pass function as an argument

We will illustrate by creating two functions. The greet function will be returning the string Hello World and the call function will be accepting a function as an argument then return the argument.

def greet():
    return "Hello World"

def call_function(function):
    return function()

print(call_function(greet))
Enter fullscreen mode Exit fullscreen mode

The output for the above code is Hello World

3. Nest a function

Python also allows for nested functions. This concept is crucial in decorators.

def main():
    def greet():
        return "Hello World"
    return greet()

print(main())
Enter fullscreen mode Exit fullscreen mode

The output if the above function is Hello World

4. Returning functions in other functions

A function can also be generated by another function.

def main():
    def say_hello():
        return "Hello"
    return say_hello
main_func = main()
print(main_func())
Enter fullscreen mode Exit fullscreen mode

The output is Hello

Creating decorators

Having gone through the concept of functions as first class objects, we will now create our first decorator. A python decorator is much similar to nested function. For this we will create two functions, an inner function and an outer function, where the inner function is nested inside the outer function.

def make_lowercase(function):
    def wrapper():
        func = function()
        return func.lower()
    return wrapper
Enter fullscreen mode Exit fullscreen mode

We can clearly see that our decorator takes a function as an argument. We shall now create a function and pass it to our decorator function.

def say_hello():
    return "HELLO WORLD"

decorator = make_lowercase(say_hello)
print(decorator())
Enter fullscreen mode Exit fullscreen mode

The output is hello world

Python also provides another way to assign decorators to functions. We can use the @ symbol followed by the name of the decorator.

@make_lowercase
def say_hello():
    return "HELLO WORLD"

print(say_hello())
Enter fullscreen mode Exit fullscreen mode

The output is Hello World

Using multiple decorators

We can also apply multiple decorators in our python functions. For example let's say that we want also to delay our function execution by 5 seconds. Then we will define a decorator function for delaying function execution as follows.

import time
def delay(function):
    def wrapper():
        time.sleep(5)
        return function()
    return wrapper
Enter fullscreen mode Exit fullscreen mode
@delay
@make_lowercase
def say_hello():
    return "HELLO WORLD"

print(say_hello())
Enter fullscreen mode Exit fullscreen mode

The output still remains Hello World but the execution of the function say_hello is delayed by 5 seconds.

Defining decorator functions with arguments

We can also define a decorator function that accepts arguments. We will do this by defining arguments in the wrapper function, where this arguments will be passed to the function being decorated at run time.

def make_title(function):
    def wrapper(arg1):
        function(arg1.title())
    return wrapper

@make_title
def book(title):
    print(f"The title of the book is {title}")

book("broken glass")
Enter fullscreen mode Exit fullscreen mode

The output of the above snippet is The title of the book is Broken Glass where the argument python is in title format.

Decorator functions with *args and **kwargs

We can define a decorator function that accepts *args and **kwargs. The *args stores all the positional arguments and the **kwargs stores all the keyword arguments.

def decorate(function):
    def wrapper(*args, **kwargs):
        print(f"The args are {args}")
        print(f"The kwargs are {kwargs}")
        function(*args, **kwargs)
    return wrapper

@decorate
def function_with_no_arguments():
    print("Has no arguments")

function_with_no_arguments()
Enter fullscreen mode Exit fullscreen mode

We have defined a decorator that accepts arguments and passes the arguments to the decorated function. We have also defined a function named function_with_no_arguments which accepts no arguments and decorated it with the decorator function. The output of the function is

The args are ()
The kwargs are {}
Has no arguments
Enter fullscreen mode Exit fullscreen mode

Let now decorate a function with positional arguments.

@decorate
def function_with_arguments(arg1, arg2):
    print("This function has arguments")

function_with_arguments("Python", "Java")
Enter fullscreen mode Exit fullscreen mode

The output of the function_with_arguments is:

The args are ('Python', 'Java')
The kwargs are {}
This function has arguments
Enter fullscreen mode Exit fullscreen mode

We can see that the args Python and Java are printed to the console.

Let's see how a function with keyword arguments will look like.

@decorate
def function_with_kwargs(**kwargs):
    print("Has kwargs")

function_with_kwargs(car="Subaru", model="SUBARU CROSSTREK SUV 2021")
Enter fullscreen mode Exit fullscreen mode

The output of the function_with_arguments is:

The args are ()
The kwargs are {'car': 'Subaru', 'model': 'SUBARU CROSSTREK SUV 2021'}
Has kwargs
Enter fullscreen mode Exit fullscreen mode

We can see that the args Python and Java are printed to the console.

Conclusion

We have seen how define function as first class objects in python. Also we have seen how to define decorators in python. Decorator in python help in observing the DRY concept in python, that is Don't Repeat Yourself. Decorators in python has various use cases like:

  1. Caching using the inbuilt @lru_cache
  2. Used in frameworks such as django, flask and fastapi

Top comments (0)