Does the Python standard library have a shortcut for writing decorators which accept arguments?
For example, if I want to write a decorator like with_timeout(timeout)
:
@with_timeout(10.0)
def cook_eggs(eggs):
while not eggs.are_done():
eggs.cook()
I have to write something like:
def with_timeout(timeout):
_func = [None]
def with_timeout_helper(*args, **kwargs):
with Timeout(timeout):
return _func[0](*args, **kwargs)
def with_timeout_return(f):
return functools.wraps(f)(with_timeout_helper)
return with_timeout_return
But that's awfully verbose. Is there a shortcut which makes decorators which accept arguments easier to write?
Note: I realize that it's also possible to use three nested functions to implement decorators with arguments… But that feels just a bit suboptimal too.
For example, possibly something like a @decorator_with_arguments
function:
@decorator_with_arguments
def timeout(f, timeout):
@functools.wraps(f)
def timeout_helper(*args, **kwargs):
with Timeout(timeout):
return f(*args, **kwargs)
return timeout_helper
The decorator arguments are accessible to the inner decorator through a closure, exactly like how the wrapped() inner function can access f . And since closures extend to all the levels of inner functions, arg is also accessible from within wrapped() if necessary.
wraps() is a decorator that is applied to the wrapper function of a decorator. It updates the wrapper function to look like wrapped function by copying attributes such as __name__, __doc__ (the docstring), etc. Parameters: wrapped: The function name that is to be decorated by wrapper function.
A decorator in Python is a function that takes another function as its argument, and returns yet another function . Decorators can be extremely useful as they allow the extension of an existing function, without any modification to the original function source code.
I tend to write my decorators as classes to be honest
class TestWithArgs(object):
def __init__(self, *deco_args, **deco_kwargs):
self.deco_args = deco_args
self.deco_kwargs = deco_kwargs
def __call__(self, func):
def _wrap(self, *args, **kwargs):
print "Blah blah blah"
return func(*args, **kwargs)
return _wrap
Its nothing if not slightly clearer
I know you said it feels suboptimal but I still feel that using three nested models is the cleanest solution. The inner two functions are just the 'normal' way of defining a decorator for a function that takes arguments (see example in python's docs for @wraps). The outer one is really just a function that takes and argument and returns a decorator.
def with_timeout(timeout):
def decorator(f):
@wraps(f)
def wrapper(*args, **kwargs):
with Timeout(timeout):
return f(*args, **kwargs)
return wrapper
return decorator
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With