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(timeo
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
First, we can define a little meta-decorator:
def decorator_with_arguments(wrapper):
return lambda *args, **kwargs: lambda func: wrapper(func, *args, **kwargs)
That allows us to create decorators that accept arguments like so:
@decorator_with_arguments
def my_wrapper(func, *decorator_args, **decorator_kwargs):
def wrapped(*call_args, **call_kwargs):
print "from decorator:", decorator_args, decorator_kwargs
func(*call_args, **call_kwargs)
return wrapped
Which can then be used normally:
@my_wrapper(1, 2, 3)
def test(*args, **kwargs):
print "passed directly:", args, kwargs
test(4, 5, 6)
Adding functools.wraps
decoration is left as an exercise :)
Another take, without using lambdas:
def decorator_with_arguments(f):
@functools.wraps(f)
def with_arguments_helper(*args, **kwargs):
def decorator(g):
return f(g, *args, **kwargs)
return decorator
return with_arguments_helper
Based on Jakob's suggestion, I've implemented a small Decorator
class, which I feel does a fairly decent job:
class Decorator(object):
def __call__(self, f):
self.f = f
return functools.wraps(f)(lambda *a, **kw: self.wrap(*a, **kw))
def wrap(self, *args, **kwrags):
raise NotImplemented("Subclasses of Decorator must implement 'wrap'")
class with_timeout(Decorator):
def __init__(self, timeout):
self.timeout = timeout
def wrap(self, *args, **kwargs):
with Timeout(timeout):
return self.f(*args, **kwargs)
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