</>StackKit
</>StackKit

Developer tutorials & guides

Python decorators explained with real-world examples

A practical guide to Python decorators explained with real-world examples.

N

Nitheesh DR

Founder & Full-Stack Engineer

4 min read633 words
#python#tutorial#guide
{
  "title": "Python Decorators Demystified",
  "description": "Unlock the full potential of Python decorators with real-world examples and expert insights. Master this powerful tool to write more efficient, readable, and maintainable code.",
  "content": "
# Python Decorators Demystified

Imagine you're working on a complex data processing pipeline, and you need to measure the execution time of each function. You could add timing code to every function, but that would lead to duplicated code and a maintenance nightmare. That's where Python decorators come in – a powerful tool that allows you to modify or extend the behavior of a function without changing its source code.

## What are Decorators?

In Python, a decorator is a small function that takes another function as an argument and returns a new function that "wraps" the original function. The new function produced by the decorator is then called instead of the original function when it's invoked.

### A Simple Example

Let's start with a basic example. We'll create a decorator that prints a message before and after the original function is called.

```python
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()

Output:

Something is happening before the function is called.
Hello!
Something is happening after the function is called.

As you can see, the decorator added new behavior to the say_hello function without modifying its source code.

Real-World Example: Timing Functions

Now, let's create a decorator that measures the execution time of a function.

import time
from functools import wraps

def timing_decorator(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        start_time = time.time()
        result = func(*args, **kwargs)
        end_time = time.time()
        print(f"Function {func.__name__} took {end_time - start_time} seconds to execute.")
        return result
    return wrapper

@timing_decorator
def slow_function():
    time.sleep(2)  # Simulate a slow operation

slow_function()

Output:

Function slow_function took 2.002344 seconds to execute.

This decorator uses the time module to measure the execution time of the slow_function. The wraps decorator from the functools module is used to preserve the original function's metadata (name, docstring, etc.).

Common Mistakes

  • Not using @wraps: When creating a decorator, it's essential to use @wraps to preserve the original function's metadata. Without it, the decorated function will lose its original name and docstring.
  • Not handling arguments: When creating a decorator, make sure to handle the original function's arguments correctly. Use *args and **kwargs to pass arguments to the original function.
  • Not returning the result: Don't forget to return the result of the original function from the decorator.

Pro Tips

  • Use decorators to implement Aspect-Oriented Programming (AOP): Decorators can be used to implement AOP concepts, such as logging, security, and caching.
  • Use decorators to create higher-order functions: Decorators can be used to create higher-order functions that take other functions as arguments and return new functions.
  • Use @lru_cache for memoization: The @lru_cache decorator from the functools module can be used to memoize the results of a function, which can improve performance by avoiding redundant calculations.

What I'd Actually Use

For most use cases, I'd recommend using the @wraps decorator from the functools module to preserve the original function's metadata. I'd also use the @lru_cache decorator for memoization.

However, for more complex use cases, I'd consider using a library like decorator or wrapt, which provide more advanced features and flexibility.

Conclusion

Python decorators are a powerful tool that can help you write more efficient, readable, and maintainable code. By mastering decorators, you can implement AOP concepts, create higher-order functions, and improve performance.

Next steps:

  • Experiment with different decorator patterns and use cases.
  • Read the official Python documentation on decorators.
  • Explore libraries like decorator and wrapt for more advanced features. " }

Tagged

#python#tutorial#guide
N

Written by

Nitheesh DR

Founder & Full-Stack Engineer

Nitheesh is a full-stack software engineer based in Tamil Nadu, India, with hands-on experience building production SaaS applications using Next.js, TypeScript, React, Node.js, and cloud infrastructure. He founded StackKit to share the practical knowledge he uses every day — not just theory, but the real-world techniques that help developers ship better software faster.