🎓 A1Learner
🔍

Python Decorators With Arguments

A decorator factory is a function that takes settings and returns a decorator.

Layer one takes the setting

The outer function is the factory. This first sketch only passes the function through.

def repeat(times):
    def decorator(func):
        return func
    return decorator

Layers two and three do the work

The decorator receives the function. The wrapper runs it the requested number of times.

from functools import wraps
def repeat(times):
    def decorator(func):
        @wraps(func)
        def wrapper():
            for _ in range(times):
                func()
        return wrapper
    return decorator

Call the factory in the @ line

The brackets call the factory first. Its returned decorator then wraps greet.

@repeat(times=2)
def greet():
    print("Hello!")
greet()

A second factory takes a label

The tag factory works the same way. Its setting is a word instead of a number.

def tag(label):
    def decorator(func):
        @wraps(func)
        def wrapper():
            print("[" + label + "]")
            func()
        return wrapper
    return decorator

Use the label factory

The label prints before the function runs. The function keeps its own job.

@tag("sale")
def show_item():
    print("Red shoes: 40")
show_item()

Example

from functools import wraps


def repeat(times):
    def decorator(func):
        @wraps(func)
        def wrapper():
            for _ in range(times):
                func()
        return wrapper
    return decorator


def tag(label):
    def decorator(func):
        @wraps(func)
        def wrapper():
            print("[" + label + "]")
            return func()
        return wrapper
    return decorator


@repeat(times=3)
def cheer():
    print("Go team!")


@tag("sale")
def show_item():
    print("Red shoes: 40")
    return "shown"


cheer()
result = show_item()
print(result)
print(cheer.__name__)

Key points

  • Decorator factory: a function that takes settings and returns a decorator
  • Three layers: the factory takes settings, the decorator takes the function, the wrapper runs the call
  • Use a decorator factory when a decorator needs a count, a label, or another setting
  • The factory call runs before decoration.
  • Its result is the decorator that wraps the function.
  • Optional-argument decorators work with or without brackets.
  • Treat that form as a tricky variant.

In this example, repeat and tag are both decorator factories. Calling repeat with 3 returns a decorator. That decorator wraps cheer in a looping wrapper. Calling tag with "sale" returns another decorator. That decorator wraps show_item in a labelling wrapper. The cheer call prints its line three times. The show_item call prints its label first. It then prints the item and returns a word. The final prints show that returned word. They also show the preserved function name.

Note

Writing @repeat alone passes the function as times.

Always call the factory, as in @repeat(times=3).

Try it Yourself

Edit the code, then press Run.

Running is simulated · Python Playground

Exercise

In @repeat(times=3), what does repeat(times=3) return?