Python Decorators Explained: How to Wrap Your Functions

3D illustration of a function being encased in protective armor layers, representing Python decorators wrapping a function.

You’ve seen them before. In Flask, you use @app.route("/"). In Django, you might see @login_required. These are Decorators, and understanding Python decorators can significantly enhance your coding skills.

A decorator is a way to “wrap” extra functionality around a function without changing the function’s actual code.

The “Manual” Way (Without @)

Imagine you want to log every time a function is called.

def my_function():
    print("Doing the real work...")

def log_wrapper(func):
    def wrapper():
        print("LOG: Function is about to run...")
        func()
        print("LOG: Function finished.")
    return wrapper

# Wrap it manually
logged_function = log_wrapper(my_function)
logged_function()

The “Decorator” Way (With @)

Python gives us the @ syntactic sugar to do this cleanly.

def log_decorator(func):
    def wrapper():
        print(f"LOG: Running {func.__name__}...")
        func()
        print("LOG: Finished.")
    return wrapper

@log_decorator
def say_hello():
    print("Hello, world!")

@log_decorator
def do_math():
    print(f"2 + 2 = {2 + 2}")

# Now just call them normally!
say_hello()
do_math()

Output:

LOG: Running say_hello...
Hello, world!
LOG: Finished.
LOG: Running do_math...
2 + 2 = 4
LOG: Finished.

We added logging to two totally different functions without changing a single line of their internal code!

Similar Posts

Leave a Reply