Decorators in Python: A Complete Guide

A S VINAY KUMARA S VINAY KUMAR
3 min read

Introduction to Decorators

Decorators in Python are a powerful tool that allows modifying or extending the behavior of functions without altering their actual implementation. They make code more readable, reusable, and maintainable by allowing modifications to functions in a clean and efficient way.

Why Use Decorators?

  • Code Reusability: Avoid repeating code for similar functionalities.

  • Separation of Concerns: Keeps function logic independent of additional behavior.

  • Enhanced Readability: Makes functions cleaner by abstracting modifications.

  • Useful for Logging, Authentication, Timing Functions, and More.

Basic Syntax of a Decorator

A decorator is a function that takes another function as input and extends its behavior.

Step 1: Creating a Basic Decorator

def my_decorator(func):
    def wrapper():
        print("Something before the function runs.")
        func()
        print("Something after the function runs.")
    return wrapper

@my_decorator
def say_hello():
    print("Hello, World!")

say_hello()

Output:

Something before the function runs.
Hello, World!
Something after the function runs.

How It Works

  • my_decorator(func) receives the function say_hello().

  • wrapper() adds additional behavior before and after calling func().

  • @my_decorator applies the decorator to say_hello(), modifying its behavior.

Decorators with Arguments

If the decorated function takes arguments, use *args and **kwargs inside the wrapper.

def smart_decorator(func):
    def wrapper(*args, **kwargs):
        print("Executing decorated function...")
        result = func(*args, **kwargs)
        print("Finished execution.")
        return result
    return wrapper

@smart_decorator
def add(a, b):
    return a + b

print(add(5, 3))

Output:

Executing decorated function...
Finished execution.
8

Built-in Decorators in Python

Python provides several built-in decorators, commonly used for modifying methods in classes.

1. @staticmethod

Defines a function inside a class that doesn’t require access to instance attributes.

class MathOperations:
    @staticmethod
    def add(a, b):
        return a + b

print(MathOperations.add(5, 3))  # Output: 8

2. @classmethod

Allows methods to receive the class itself (cls) instead of instance (self).

class MyClass:
    class_var = "Hello"

    @classmethod
    def show_class_var(cls):
        return cls.class_var

print(MyClass.show_class_var())  # Output: Hello

3. @property

Used to define read-only properties in a class.

class Person:
    def __init__(self, name):
        self._name = name

    @property
    def name(self):
        return self._name

p = Person("Mahesh")
print(p.name)  # Output: Mahesh

Using Multiple Decorators

You can stack multiple decorators on a single function:

def decorator1(func):
    def wrapper():
        print("Decorator 1 before function")
        func()
        print("Decorator 1 after function")
    return wrapper

def decorator2(func):
    def wrapper():
        print("Decorator 2 before function")
        func()
        print("Decorator 2 after function")
    return wrapper

@decorator1
@decorator2
def hello():
    print("Hello, World!")

hello()

Output:

Decorator 1 before function
Decorator 2 before function
Hello, World!
Decorator 2 after function
Decorator 1 after function

Real-World Applications of Decorators

1. Logging Function Execution

import time

def log_decorator(func):
    def wrapper(*args, **kwargs):
        print(f"Executing {func.__name__} at {time.strftime('%X')}")
        return func(*args, **kwargs)
    return wrapper

@log_decorator
def greet(name):
    print(f"Hello, {name}!")

greet("Mahesh")

2. Checking User Authentication

def requires_authentication(func):
    def wrapper(user, *args, **kwargs):
        if user == "admin":
            return func(*args, **kwargs)
        else:
            print("Access Denied")
    return wrapper

@requires_authentication
def secure_action():
    print("Performing secure action...")

secure_action("user")   # Output: Access Denied
secure_action("admin")  # Output: Performing secure action...

3. Timing Function Execution

import time

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

@timer_decorator
def slow_function():
    time.sleep(2)
    print("Finished processing.")

slow_function()

Output:

Finished processing.
slow_function took 2.0001 seconds
1
Subscribe to my newsletter

Read articles from A S VINAY KUMAR directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

A S VINAY KUMAR
A S VINAY KUMAR