Decorators, with optional arguments
Nathan Rice
nathan.alexander.rice at gmail.com
Fri Jul 2 14:58:46 EDT 2010
I like to think of decorators with arguments as decorator factory functions.
I try and unroll them as much as possible... I have some decorators that
work like so (and please note that the wraps and returns_as_output are
separate so that I can mutate the behavior as needed, if you just wanted a
single decorator this could be condensed):
def wrap(f, wrapping_function, *args):
argspec = inspect.getargspec(f)
without_defaults = len(argspec.args) - len(argspec.defaults or [])
f._args = {}
f._varkw = []
f._arg_defaults = (None,) * without_defaults + (argspec.defaults or
tuple())
for arg in args:
if arg in argspec.args:
f._args[arg] = argspec.args.index(arg)
else:
if argspec.keywords:
f._varkw.append(arg)
return decorator.decorator(wrapping_function, f)
def return_as_output(output_f):
def func(f, *args, **kwargs):
new_args = list(args)
for (dict_name, index) in f._args.items():
if len(args) > index:
new_args[index] = output_f(args[index])
else:
d = f._arg_defaults[index]
kwargs[dict_name] = output_f(kwargs.pop(dict_name, d))
for dict_name in f._varkw:
kwargs[dict_name] = output_f(kwargs.pop(dict_name, None))
return f(*new_args, **kwargs)
return func
def iterables(*args):
'''
Decorator that guarantees that the named arguments will be iterable.
'''
as_iterables = return_as_output(iter_)
return lambda f: wrap(f, as_iterables, *args)
Just as an example... I have a family of decorators that check compliance
with abstract base classes, then try to mutate variables to provide it if
duck typing would fail. I don't know if this helps, it is kind of
convoluted, but hopefully it gives you another example to work from.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/python-list/attachments/20100702/03ee77d4/attachment-0001.html>
More information about the Python-list
mailing list