6: Decorators

Summary

  • Decorators are functions that take a single function as an argument and return a single function
    • They are used to transform functions

  • A decorator is written as @dec in front of a function definition and that is equivalent to f = dec(f) where f is the name of the function

  • Some decorators modify the unction by returning a new wrapper function object

  • Some decorators add or modify attributes of the function object and do not return a new function object

  • Multiple decorators are applied starting with the decorator closest to the function working up the page

  • You can also use a decorator factory with parameters
    • It is evaluated at once and has to return a decorator, which is applied to the function in the usual way

  • The main use of a decorator factory is to provide parameters to a decorator

  • Decorators that return wrappers are more complicated because they change the function object
    • The standard wraps method can be used to transfer attributes from the original function object to the new object

Program

from functools import wraps

# ---------------------------------------
# 1️⃣ Simple decorator (returns wrapper)
# ---------------------------------------
def simple_logger(func):
    """Decorator that wraps a function and logs before calling it."""

    @wraps(func)  # preserves original function metadata
    def wrapper(*args, **kwargs):
        print(f"Calling function: {func.__name__}")
        return func(*args, **kwargs)

    return wrapper


# ---------------------------------------------------
# 2️⃣ Decorator that modifies attributes (no wrapper)
# ---------------------------------------------------
def add_author(func):
    """Decorator that adds an attribute to the function."""
    func.author = "Brayden"
    return func  # returns same function object


# ---------------------------------------
# 3️⃣ Decorator factory (takes parameter)
# ---------------------------------------
def repeat(times):
    """Decorator factory that repeats function execution."""

    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            for _ in range(times):
                result = func(*args, **kwargs)
            return result
        return wrapper

    return decorator  # must return a decorator


# ---------------------------------
# 4️⃣ Using multiple decorators
# Closest decorator runs first
# ---------------------------------
@add_author
@simple_logger
@repeat(2)
def greet(name):
    """Greets a person."""
    print(f"Hello {name}!")


# ---------------------------------
# Main Program
# ---------------------------------
greet("Alice")

print("\nFunction name:", greet.__name__)
print("Author attribute:", greet.author)

Program Output

(docs-env) root@BMitchellLTOP:~/git/sphinx_students/source/programming_lang/book/programs# python3 chapterSix.py
Calling function: greet
Hello Alice!
Hello Alice!

Function name: greet
Author attribute: Brayden