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:

  1. create decorator normal.
  2. after first target function argument, add optional arguments.
  3. 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

Popular posts from this blog

ASP.NET/SQL find the element ID and update database -

jquery - appear modal windows bottom -

c++ - Compiling static TagLib 1.6.3 libraries for Windows -