Fundamentals 7 min read

Python Decorators: A Comprehensive Guide with Practical Examples

This article explains Python decorators, covering their purpose and providing ten practical examples—including timing, logging, caching, type checking, singleton, retry, profiling, authentication, asynchronous, and input validation—each illustrated with complete, ready‑to‑run code snippets.

Test Development Learning Exchange
Test Development Learning Exchange
Test Development Learning Exchange
Python Decorators: A Comprehensive Guide with Practical Examples

Python decorators are special functions that can modify or enhance other functions without altering the original source code, enabling features such as logging, performance testing, permission checks, caching, and transaction handling.

1. Timing Decorator (@timer) – Measures function execution time.

import time

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

@timer
def example_function():
    time.sleep(1)

example_function()

2. Logging Decorator (@log_execution) – Records function calls and results.

import logging
logging.basicConfig(level=logging.INFO)

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

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

add(3, 5)

3. Caching Decorator (@lru_cache) – Uses functools.lru_cache to cache results and avoid repeated calculations.

from functools import lru_cache

@lru_cache(maxsize=None)
def fibonacci(n):
    if n < 2:
        return n
    else:
        return fibonacci(n-1) + fibonacci(n-2)

print(fibonacci(10))

4. Type‑Check Decorator (@type_check) – Ensures function arguments match expected types.

def type_check(*arg_types):
    def decorator(func):
        def wrapper(*args, **kwargs):
            for arg, expected_type in zip(args, arg_types):
                if not isinstance(arg, expected_type):
                    raise TypeError(f"Argument {arg} is not of type {expected_type}")
            return func(*args, **kwargs)
        return wrapper
    return decorator

@type_check(int, int)
def multiply(x, y):
    return x * y

print(multiply(2, 3))

5. Singleton Decorator (@singleton) – Guarantees a class has only one instance.

class SingletonMeta(type):
    _instances = {}
    def __call__(cls, *args, **kwargs):
        if cls not in cls._instances:
            cls._instances[cls] = super().__call__(*args, **kwargs)
        return cls._instances[cls]

class Singleton(metaclass=SingletonMeta):
    pass

s1 = Singleton()
s2 = Singleton()
print(s1 is s2)

6. Retry Decorator (@retry_on_exception) – Automatically retries a function when specified exceptions occur.

import random, time

def retry_on_exception(max_retries=3, exceptions=(Exception,), delay=1):
    def decorator(func):
        def wrapper(*args, **kwargs):
            retries = 0
            while retries < max_retries:
                try:
                    return func(*args, **kwargs)
                except exceptions as e:
                    print(f"Caught {e}. Retrying...")
                    retries += 1
                    time.sleep(delay)
            raise Exception("Max retries exceeded.")
        return wrapper
    return decorator

@retry_on_exception(max_retries=3)
def might_fail():
    if random.randint(0, 2) == 0:
        raise ValueError("Failed.")
    print("Success!")

might_fail()

7. Performance‑Profiling Decorator (@profile) – Uses cProfile to profile function execution.

import cProfile

def profile(func):
    def wrapper(*args, **kwargs):
        profiler = cProfile.Profile()
        profiler.enable()
        result = func(*args, **kwargs)
        profiler.disable()
        profiler.print_stats()
        return result
    return wrapper

@profile
def dummy_function(n):
    sum([i**2 for i in range(n)])

dummy_function(100000)

8. Authentication Decorator (@authenticate) – Checks whether a user is logged in before allowing function execution.

def authenticate(user_required=True):
    def decorator(func):
        def wrapper(*args, **kwargs):
            if not user_required or user_is_logged_in():
                return func(*args, **kwargs)
            else:
                raise PermissionError("User not authenticated.")
        return wrapper
    return decorator

@authenticate
def sensitive_operation():
    print("Sensitive operation performed.")

sensitive_operation()

9. Asynchronous Decorator (@async_decorator) – Wraps async functions to add pre‑ and post‑execution behavior.

import asyncio

async def async_decorator(func):
    async def wrapper(*args, **kwargs):
        print("Starting async task...")
        result = await func(*args, **kwargs)
        print("Async task finished.")
        return result
    return wrapper

@async_decorator
async def long_running_task():
    await asyncio.sleep(2)
    return "Task done."

asyncio.run(long_running_task())

10. Input‑Validation Decorator (@validate_input) – Ensures function arguments fall within a specified range.

def validate_input(minimum=0, maximum=100):
    def decorator(func):
        def wrapper(value):
            if not (minimum <= value <= maximum):
                raise ValueError(f"Value must be between {minimum} and {maximum}")
            return func(value)
        return wrapper
    return decorator

@validate_input(1, 10)
def process_value(value):
    print(f"Processing value: {value}")

process_value(5)
Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Pythonprogrammingcachinglogging
Test Development Learning Exchange
Written by

Test Development Learning Exchange

Test Development Learning Exchange

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.