Menu Close

A Comprehensive Guide to Decorators in Python

Decorators in Python

Hello Developers, In this article, You will learn all about the most important Python topic called decorator in Python. Decorators in Python are the most famous and important because they provide the functionality to change the functionality or behavior of the existing Python code without altering the actual code.

Sometimes we have a function and we want to increase their functionality without a coding change in that function, So in that scenario decorator comes into the picture because the use of a decorator can increase the functionality of the existing function without altering the code.

For FYI, a Function in Python is a first-class object, Which means the functions in Python can be assigned to a variable or can be returned by another function, passed as a parameter to a function, or defined into another function.

That’s why it is very important to know some interesting facts about Python functions before going through decorators in Python.

Let’s start.

Assign function to the variable

Being a first-class object, Python functions can be assigned to the variable, and that variable can be used as a function.
Let’s see how can I do that.

Examples:

def Addition(x, y):
	print(f"Addition of {x} and {y} is:- {x + y}")


# Without assigning a function to the variable.
Addition(12, 34)

# Assign a function to the variable.
x = Addition
x(12, 30)

Output


The addition of 12 and 34 is:- 46
The addition of 12 and 30 is:- 42

Defining a function inside another function

Python function can be defined into another Python function. As you can see in the below example, How I have defined the sum() function inside the Addition() function.

Example:

def Addition(a, b):
	def sum():
		return f"Addition of {a} and {b} is:- {a + b}"		
	return sum

result = Addition(20, 30)
print(result())

Output

The addition of 20 and 30 is:- 50

Passing function as an argument to the function

As we know first is a first-class object in Python that can be passed as an argument to another function.

Example:

def Message(msg):
	return msg()
		
def welcome():
	print("Welcome to the programming funda")
	
Message(welcome)

Output

Welcome to the programming funda

Now, after learning all the above concepts we are ready to learn about Python decorators.

What are Decorators in Python?

Decorators in Python are a powerful tool that allows Python programmers to change the behavior of the existing Python function or class without changing the function and class’s actual code. May you be confused with this definition but wait, After this tutorial, you will completely get rid of this confusion.

The decorator takes a function as an argument adds some functionality and returns it.

In Python, decorators are implemented as functions (or classes) that take another function as an argument and return a new function that enhances or alters the original one. This approach adheres to the principle of separation of concerns and can make code more modular, reusable, and readable.

Why Decoartos are Useful Python

There are several reasons for decorators to be useful in Python some of them are listed below as you can see.

  • Code Reusability: Decorators promote code reusability by allowing you to apply the same enhancement logic to multiple functions without duplicating code.
  • Separation of Concerns: They help separate the core functionality of a function from the additional features or behaviors, making your code cleaner and more maintainable.
  • Enhanced Functionality: Decorators can add, modify, or extend the behavior of functions or methods dynamically. This can be useful for logging, access control, instrumentation, caching, and more.
  • Cleaner Code: By using decorators, you can keep your main logic cleaner and more focused on its core responsibilities, while the decorators handle the auxiliary tasks.

Now Let’s understand the Python decorators by examples.

Python Decorators Examples

Now let’s see some Python decoratos examples and explanations so that you can get more clarity about the decoratos in Python.

Example:

def smart(any_function):
    def inner():
        print("Before execution")
        any_function()
        print("After execution")

    return inner

def func2():
    print("Inside the function")

var = smart(func2)
var()

Output

Before execution
Inside the function
After execution

Let’s break down the code to get more insights.

  1. Defining the decoration called ‘smart‘: The ‘smart’ is a decorator function that takes a function ‘any_function‘ as an argument and inside the ‘smart‘ function there is another function defined called ‘inner‘.
    • smart(any_function):- It accepts a function any_function and defines a nested function inner.
    • inner() function:
      • This is the function that will be returned by the smart decorator.
      • It prints "Before execution", then calls any_function(), and finally prints the "After execution" message.
    • return inner:- The inner function is returned from the smart function which means it is replacing the original function (func2) with inner.
  2. Defining func2 function:- The func2 is just a normal function that prints a simple message ‘Inner function‘.
  3. Applying the Decorator:- Using var = smart(func2) to apply the decorator.
    • Here, smart(func2) is called. This applies the smart decorator to func2, and it returns the inner function with func2 bound as any_function.
    • var now holds a reference to the inner function.
  4. Calling the Decorated Function: Using var() to call the decorated function.
  5. After calling the var(), the above sequence will be triggered and the following output will be generated.
Before execution
Inside the function
After execution

Python provides another way to use the decorator using @decoratorname.Instead of assigning decorator to a variable just use the @decoratorname with the function on which you want to apply decorator.

Let’s see

Example:

def smart(any_function):
    def inner():
        print("Before execution")
        any_function()
        print("After execution")

    return inner


@smart
def func2():
    print("Inside the function")


func2()

Output


Before execution
Inside the function
After execution

Here, I have created a function decorator ‘upper_case‘ that is capable of converting the string to upper case.

Example:

def upper_case(myfunc):
	result = myfunc()
	print(result.upper())
	

@upper_case		
def message():
	return "Welcome to Programming Funda Python tutorial."

Output

WELCOME TO PROGRAMMING FUNDA PYTHON TUTORIAL.

A function return value

In all the above examples, The inner or nested function inside the function decorator is not returning any value but here we will see what will happen if a nested function returns some value.

def smart(any_function):
    def inner(*args, **kwargs):
	
        print("Before execution")
        return_value = any_function(*args)
        print("After execution")
        return return_value

    return inner

@smart
def func2(a, b):
    print("Inside the function")
    return a + b

	
print(f"Sum :- {func2(12, 23)}")

When you run the above program you will get output like this:


Before execution
Inside the function
After execution
Sum:- 35

Break down of the above example:

  • smart(any_function): The ‘smart’ is a function decorator that takes a function as an argument.
  • def inner(*args, **kwargs):- The ‘inner’ is a nested function or an inner function that takes any number of positional arguments (*args) and keyword arguments (**kwargs).
    • print a message ‘Before execution‘ before calling the original function.
    • Call the original function (any_function) with provided arguments and capture the output value in a variable return_value.
    • print a message ‘After execution‘ after calling the original function.
    • return the value stored inside the return_value variable.
  • Decorating the original function (func2) with @smart.
    • Apply the smart decorator to the func2 function by using @smart and the func2 function is replaced by the inner function returned by the smart. Actually @smart is the shorthand for func2 = smart(func2).
    • func2(a, b): The original function func2 takes two arguments, prints a message, and returns their sum.
  • Calling the Decorated Function
    • func2(12, 23): When func2 is called, The inner function will get executed behind the scenes because of the decorator.

This is the way you can use decorators in Python.

Let’s understand some real-life application use cases of Python decoratos.

Use Cases of Decorators in Real-Life Applications

These are some major uses of Python decorators in real-life Python applications.

  • Authorization: Decorators can enforce access control in web applications by checking user permissions before allowing function execution.
def requires_admin(func):
    def wrapper(user, *args, **kwargs):
        if not user:
            return func(user, *args, **kwargs)
        else:
            raise PermissionError("User must be an admin")
    return wrapper

@requires_admin
def allow_login(user):
    # code to perform login
    return "Admin has been logged in"


# User can be normal user or admin, It True then or normal user otherwise it will admin
user = True
print(allow_login(user))


  • Timing: Decorators can measure the time taken by a function to execute, which can be useful for performance profiling.
import time

def calculuate_execution_time(func):
    def wrapper(user, *args, **kwargs):
        if user:
            start_time = time.time()
            func(user, *args, **kwargs)
            end_time = time.time()
            print(f"Function {func.__name__} took {end_time - start_time:.4f} seconds to execute")
        else:
            raise KeyError("User not found")
    return wrapper


@calculuate_execution_time
def delete_user(user):
    # code to perform user delete operations suppose it takes 3 seconds
    time.sleep(3)

# User can be normal user or admin, It True then or normal user otherwise it will admin
user = True
delete_user(user)
  • Validation: Decorators can validate input data before a function executes, ensuring that functions are called with valid arguments.
def validate_positive(func):
    def wrapper(x, *args, **kwargs):
        if x <= 0:
            raise ValueError("Input must be positive")
        return func(x, *args, **kwargs)
    return wrapper

@validate_positive
def square_root(x):
    import math
    return math.sqrt(x)

print(square_root(16))  # Valid
Note:- 👉 Explore Python class decoratos.

Conclusion

So In this article, you have learned all about Decorators in Python along with examples and real-life use cases of the Python decoratos. Decorators in Python play the most major role when you want to change the behavior of the existing Python code without altering that actual code.

As A Python programmer, must use the decorators when we want to perform some operations in a function without changing the actual code of the function and we want to perform the same operations in multiple functions because decorators are the concepts of reusability.

A single decoratos can be applied to multiple functions if required.

If this article genuinely helps you, please share and keep visiting for further Python tutorials.

For more information:- Click Here

Happy Learning.

Types of Inheritance in Python
How to use Class Decorator in Python

Related Posts