python - Making decorators with optional arguments -
from functools import wraps def foo_register(method_name=none): """does stuff.""" def decorator(method): if method_name none: method.gw_method = method.__name__ else: method.gw_method = method_name @wraps(method) def wrapper(*args, **kwargs): method(*args, **kwargs) return wrapper return decorator
example: following decorates my_function
foo_register
instead of ever making decorator
.
@foo_register def my_function(): print('hi...')
example: following works expected.
@foo_register('say_hi') def my_function(): print('hi...')
if want work correctly in both applications (one using method.__name__
, 1 passing name in), have check inside of foo_register
see if first argument decorator, , if so, have to: return decorator(method_name)
(instead of return decorator
). sort of "check see if it's callable" seems hackish. there nicer way create multi-use decorator this?
p.s. know can require decorator called, that's not "solution". want api feel natural. wife loves decorating, , don't want ruin that.
through of answers here , elsewhere , bunch of trial , error i've found there far easier , generic way make decorators take optional arguments. check args called there isn't other way it.
the key decorate decorator.
generic decorator decorator code
here decorator decorator (this code generic , can used needs optional arg decorator):
def optional_arg_decorator(fn): def wrapped_decorator(*args): if len(args) == 1 , callable(args[0]): return fn(args[0]) else: def real_decorator(decoratee): return fn(decoratee, *args) return real_decorator return wrapped_decorator
usage
using easy as:
- create decorator normal.
- after first target function argument, add optional arguments.
- decorate decorator
optional_arg_decorator
example:
@optional_arg_decorator def example_decorator_with_args(fn, optional_arg = 'default value'): ... return fn
test cases
for use case:
so case, save attribute on function passed-in method name or __name__
if none:
@optional_arg_decorator def register_method(fn, method_name = none): fn.gw_method = method_name or fn.__name__ return fn
add decorated methods
now have decorator usable with or without args:
@register_method('custom name') def custom_name(): pass @register_method def default_name(): pass assert custom_name.gw_method == 'custom name' assert default_name.gw_method == 'default_name' print 'test passes :)'
Comments
Post a Comment