Python Decorator with Arguments

Created with Sketch.

Python Decorator with Arguments

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.

Leave a Reply

Your email address will not be published. Required fields are marked *