Summary: in this tutorial, you’ll learn how to define Python decorators with arguments using a decorator factory.
Introduction to Python decorator with arguments
Suppose that you have a function called say
that prints out a message:
def say(message):
''' print the message
Arguments
message: the message to show
'''
print(message)
Code language: Python (python)
and you want to execute the say()
function 5 times repeatedly each time you call it. For example:
say('Hi')
Code language: Python (python)
It should show the following the Hi
message five times as follows:
Hi
Hi
Hi
Hi
Hi
To do that, you can use a regular decorator:
@repeat
def say(message):
''' print the message
Arguments
message: the message to show
'''
print(message)
Code language: Python (python)
And you can define the repeat
decorator as follows:
def repeat(fn):
@wraps(fn)
def wrapper(*args, **kwargs):
for _ in range(5):
result = fn(*args, **kwargs)
return result
return wrapper
Code language: Python (python)
The following shows the complete code:
from functools import wraps
def repeat(fn):
@wraps(fn)
def wrapper(*args, **kwargs):
for _ in range(5):
result = fn(*args, **kwargs)
return result
return wrapper
@repeat
def say(message):
''' print the message
Arguments
message: the message to show
'''
print(message)
say('Hello')
Code language: Python (python)
What if you want to execute the say()
function repeatedly ten times. In this case, you need to change the hard-coded value 5 in the repeat
decorator.
However, this solution isn’t flexible. For example, you want to use the repeat
decorator to execute a function 5 times and another 10 times. The repeat
decorator would not meet the requirement.
To fix this, you need to change the repeat
decorator so that it accepts an argument that specifies the number of times a function should execute like this:
@repeat(5)
def say(message):
...
Code language: Python (python)
To define the repeat
decorator, the repeat(5)
should return the original decorator.
def repeat(times):
# return the original "repeat" decorator
Code language: Python (python)
The new repeat
function returns a decorator. And it’s often referred to as a decorator factory.
The following repeat
function returns a decorator:
def repeat(times):
''' call a function a number of times '''
def decorate(fn):
@wraps(fn)
def wrapper(*args, **kwargs):
for _ in range(times):
result = fn(*args, **kwargs)
return result
return wrapper
return decorate
Code language: Python (python)
In this code, the decorate
function is a decorator. It’s equivalent to the original repeat
decorator.
Note that the new repeat function isn’t a decorator. It’s a decorator factory that returns a decorator.
Put it all together.
from functools import wraps
def repeat(times):
''' call a function a number of times '''
def decorate(fn):
@wraps(fn)
def wrapper(*args, **kwargs):
for _ in range(times):
result = fn(*args, **kwargs)
return result
return wrapper
return decorate
@repeat(10)
def say(message):
''' print the message
Arguments
message: the message to show
'''
print(message)
say('Hello')
Code language: Python (python)
Summary
- Use a factory decorator to return a decorator that accepts arguments.