⚡ AMP
Python

Python decorators explained with real-world examples

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

Nitheesh DR 4 min read
{
  "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

Pro Tips

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: