Understanding Python Decorators

Python decorators are a powerful tool that allows you to modify the behavior of a function or class method. Here’s a step-by-step guide to understanding them with examples.

Step 1: Understand Functions as First-Class Objects

In Python, functions are first-class objects, meaning they can be passed around and used as arguments, just like any other object (string, int, float, etc.).

def greet(name):
    return f"Hello, {name}!"

def call_function(func, name):
    return func(name)

print(call_function(greet, "Alice"))  # Output: Hello, Alice!

Step 2: Define a Simple Decorator

A decorator is a function that takes another function as an argument, adds some kind of functionality, and returns another function.

def simple_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

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

decorated_function = simple_decorator(say_hello)
decorated_function()

# Output:
# Something is happening before the function is called.
# Hello!
# Something is happening after the function is called.

Step 3: Use the @ Syntax for Decorators

Python provides a special syntax to apply decorators more conveniently using the @ symbol.

@simple_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.

Step 4: Decorators with Arguments

If your decorator needs to accept arguments, you need to add another layer of functions.

def decorator_with_arguments(arg):
    def decorator(func):
        def wrapper(*args, **kwargs):
            print(f"Decorator argument: {arg}")
            return func(*args, **kwargs)
        return wrapper
    return decorator

@decorator_with_arguments("Test")
def say_hello(name):
    print(f"Hello, {name}!")

say_hello("Alice")

# Output:
# Decorator argument: Test
# Hello, Alice!

Step 5: Preserve Function Metadata

When you decorate a function, the original function’s metadata (such as its name, docstring, etc.) gets lost. To preserve this metadata, use functools.wraps.

import functools

def simple_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

@simple_decorator
def say_hello():
    """This function says hello"""
    print("Hello!")

print(say_hello.__name__)  # Output: say_hello
print(say_hello.__doc__)   # Output: This function says hello

Step 6: Applying Multiple Decorators

You can apply multiple decorators to a single function by stacking them.

def decorator_one(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        print("Decorator One")
        return func(*args, **kwargs)
    return wrapper

def decorator_two(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        print("Decorator Two")
        return func(*args, **kwargs)
    return wrapper

@decorator_one
@decorator_two
def say_hello():
    print("Hello!")

say_hello()

# Output:
# Decorator One
# Decorator Two
# Hello!

Step 7: Practical Example: Timing a Function

A common use case for decorators is to measure the execution time of a function.

import time

def timer(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        start_time = time.time()
        result = func(*args, **kwargs)
        end_time = time.time()
        print(f"Elapsed time: {end_time - start_time:.4f} seconds")
        return result
    return wrapper

@timer
def slow_function():
    time.sleep(2)
    print("Function finished")

slow_function()

# Output:
# Function finished
# Elapsed time: 2.000X seconds

By following these steps, you should have a solid understanding of how decorators work in Python and how to use them effectively.

Hello, I’m Anuj. I make and teach software.

My website is free of advertisements, affiliate links, tracking or analytics, sponsored posts, and paywalls.
Follow me on LinkedIn, X (twitter) to get timely updates when I post new articles.
My students are the reason this website exists. ❤️

Feedback Display