This is a template for a function decorator that does not require () if no parameters are to be given and supports both positional and keyword parameters (but requires cheching on locals() to find out if the first parameter is the function to be decorated or not):
def multiplying(factor_or_func=None):
def _decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
if 'factor_or_func' not in locals() \
or callable(factor_or_func) \
or factor_or_func is None:
factor = 1
else:
factor = factor_or_func
return factor * func(*args, **kwargs)
return wrapper
return _decorator(factor_or_func) if callable(factor_or_func) else _decorator
@multiplying
def summing(x): return sum(x)
print(summing(range(10)))
# 45
@multiplying()
def summing(x): return sum(x)
print(summing(range(10)))
# 45
@multiplying(10)
def summing(x): return sum(x)
print(summing(range(10)))
# 450
Alternatively, if one does not need positional arguments, one can relax the need for checking on the first parameter within the wrapper() (thus removing the need to use locals()):
import functools
def decorator(_func=None, **decorator_kws):
def _decorator(func):
@functools.wraps(func)
def wrapper(*args, **kws):
return func(*args, **kws)
return wrapper
if callable(func_):
return _decorator(func_)
elif func_ is None:
return _decorator
else:
raise RuntimeWarning("Positional argument with this decorator
an example of this is given below:
import functools
def multiplying(factor_or_func=Nonefunc_=None, factor=1):
def _decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
if 'factor_or_func'return notfactor in* localsfunc()*args, \**kwargs)
return wrapper
orif callable(factor_or_funcfunc_) \:
return _decorator(func_)
orelif factor_or_funcfunc_ is None:
factor =return 1_decorator
else:
raise RuntimeWarning("Positional argument with this decorator factorhas =no factor_or_funceffect.")
@multiplying
def summing(x): return factor * funcsum(*args, **kwargsx)
return wrapper
return _decoratorprint(factor_or_func) if callablesumming(factor_or_funcrange(10) else))
# _decorator45
@multiplying()
def summing(x): return sum(x)
print(summing(range(10)))
# 45
@multiplying(factor=10)
def summing(x): return sum(x)
print(summing(range(10)))
# 45450
@multiplying(10)
def summing(x): return sum(x)
print(summing(range(10)))
# 450RuntimeWarning Traceback (most recent call last)
# ....
# RuntimeWarning: Positional argument with this decorator has no effect.
(partially reworked from @ShitalShah's answer)