Fundamentals 6 min read

Understanding Python Decorators: Concepts, Examples, and Practical Uses

This article explains Python decorators, covering their definition as higher‑order functions, essential concepts like closures, step‑by‑step examples of basic, parameterized, class‑based, and stateful decorators, as well as practical uses such as logging and caching, all illustrated with clear code snippets.

Test Development Learning Exchange
Test Development Learning Exchange
Test Development Learning Exchange
Understanding Python Decorators: Concepts, Examples, and Practical Uses

Python decorators are a powerful and flexible feature that allow you to modify or extend the behavior of functions or methods. A decorator is essentially a higher‑order function that takes a function as an argument and returns a new function, and they are commonly used for logging, performance testing, transaction handling, caching, and permission checks.

Basic concepts

Higher‑order function: a function that accepts one or more functions as input or returns a function. Closure: a function object that remembers the values of the free variables that were in scope when it was defined.

Basic decorator example

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

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

say_hello()

The @ syntax is syntactic sugar; @my_decorator is equivalent to say_hello = my_decorator(say_hello) . When say_hello() is called, the wrapper function runs.

Decorator with parameters

def repeat(num_times):
    def decorator(func):
        def wrapper(*args, **kwargs):
            for _ in range(num_times):
                result = func(*args, **kwargs)
            return result
        return wrapper
    return decorator

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

greet("Alice")

Preserving metadata

import functools

def my_decorator(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        print("Something is happening before the function is called.")
        result = func(*args, **kwargs)
        print("Something is happening after the function is called.")
        return result
    return wrapper

@my_decorator
def say_hello():
    """Say hello to the user."""
    print("Hello!")

print(say_hello.__name__)  # say_hello
print(say_hello.__doc__)   # Say hello to the user.

Class decorator

def add_class_method(cls):
    def decorator(func):
        setattr(cls, func.__name__, func)
        return cls
    return decorator

@add_class_method
class MyClass:
    pass

def say_hello(self):
    print("Hello from MyClass!")

instance = MyClass()
instance.say_hello()  # Hello from MyClass!

Decorator composition

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

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

@decorator1
@decorator2
def say_hello():
    print("Hello!")

say_hello()
# Output:
# Decorator 1
# Decorator 2
# Hello!

Stateful decorator (using a class)

class Counter:
    def __init__(self, func):
        self.func = func
        self.count = 0
    def __call__(self, *args, **kwargs):
        self.count += 1
        print(f"Function {self.func.__name__} has been called {self.count} times")
        return self.func(*args, **kwargs)

@Counter
def say_hello():
    print("Hello!")

say_hello()  # Function say_hello has been called 1 times
say_hello()  # Function say_hello has been called 2 times

Practical applications

Logging decorator:

import logging

def log_function_call(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_function_call
def add(a, b):
    return a + b

add(3, 5)

Caching with functools.lru_cache :

from functools import lru_cache

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

print(fibonacci(10))  # 55

Conclusion

Decorators are a versatile tool in Python that enable you to enhance or modify functions and classes. By mastering decorators, you can write cleaner, more modular, and maintainable code.

cachingLoggingdecoratorClosurefunctoolsHigher-order function
Test Development Learning Exchange
Written by

Test Development Learning Exchange

Test Development Learning Exchange

0 followers
Reader feedback

How this landed with the community

login 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.