Categories: python

Python Decorators

In Python, a decorator is a special kind of function that modifies the behavior of another function. Decorators are often implemented as closures, which are nested functions that can remember and access variables from their containing function, even after the containing function has finished executing.

But before diving deep into decorators let us understand some concepts that will come in handy in learning the decorators.

First Class Objects:

Functions in Python are first-class objects, which means they have the same properties and capabilities as any other object, such as integers or strings. This allows for greater flexibility and power in programming, as functions can be passed as arguments to other functions, returned from other functions, stored in data structures, and used in other ways that are typically reserved for regular objects.

Decorators:

As previously stated, decorators are used to modify the behavior of a function or class. Decorators use functions as arguments in another function, which is then called inside the wrapper function.

A decorator is applied to a function using the @decorator_name notation, which is a shorthand for calling decorator_name(function) and assigning the result back to the function.

Here is an example of a decorator function that adds logging to a function:

import logging

def log_decorator(func):
    def wrapper(*args, **kwargs):
        logging.info(f"Calling function {func.__name__} with args: {args}, kwargs: {kwargs}")
        result = func(*args, **kwargs)
        logging.info(f"Function {func.__name__} returned: {result}")
        return result
    return wrapper

@log_decorator
def add(x, y):
    return x + y

add(1, 2)
</code>

In this example, the log_decorator function takes a function as an argument and returns a wrapper function that logs the function call and its result. The @log_decorator syntax is used to apply the decorator to the add() function. When the add(1, 2) is called, it will first log the function call, execute the original add function and then log the result.

When the code runs, it will output something like this:

INFO:root:Calling function add with args: (1, 2), kwargs: {}
INFO:root:Function add returned: 3

This is a simple example, but decorators can be used in many ways to add functionality to functions and classes in a clean, reusable way.

Chaining the Decorators:

We can chain multiple decorators together to apply multiple modifications to a function. Here’s an example:

import time

def timer_decorator(func):
    def wrapper(*args, **kwargs):
        start_time = time.time()
        result = func(*args, **kwargs)
        end_time = time.time()
        print(f"Time taken: {end_time - start_time}")
        return result
    return wrapper

def log_decorator(func):
    def wrapper(*args, **kwargs):
        logging.info(f"Calling function {func.__name__} with args: {args}, kwargs: {kwargs}")
        result = func(*args, **kwargs)
        logging.info(f"Function {func.__name__} returned: {result}")
        return result
    return wrapper

@timer_decorator
@log_decorator
def add(x, y):
    return x + y

add(1, 2)

In this example, both timer_decorator and log_decorator are applied to the add() function using the @ syntax. So, when the add(1, 2) is called, it will first log the function call, then it will execute the original add function, measure the execution time and then log the result.

The order of decorators is important, as it determines the order in which the decorators’ wrapper functions are executed. In this example, log_decorator is applied first and timer_decorator is applied second, so logging happens before timing. If you want to change the order, you can swap the decorators like this:

@log_decorator
@timer_decorator
def add(x, y):
    return x + y

This way, the timing will happen before the logging.

Note: also read about Python Closures

Follow Me

Please follow me to read my latest post on programming and technology if you like my post.

https://www.instagram.com/coderz.py/

https://www.facebook.com/coderz.py

Recent Posts

Generate Parenthesis | Intuition + Code | Recursion Tree | Backtracking | Java

Problem Statement: Given n pairs of parentheses, write a function to generate all combinations of well-formed parentheses. Example…

2 months ago

Square Root of Integer

Given an integer A. Compute and return the square root of A. If A is…

1 year ago

Build Array From Permutation

Given a zero-based permutation nums (0-indexed), build an array ans of the same length where…

1 year ago

DSA: Heap

A heap is a specialized tree-based data structure that satisfies the heap property. It is…

1 year ago

DSA: Trie

What is a Trie in DSA? A trie, often known as a prefix tree, is…

1 year ago

Trees: Lowest Common Ancestor

What is the Lowest Common Ancestor? In a tree, the lowest common ancestor (LCA) of…

1 year ago