I write decorators a lot in my work. Sometimes you need to write "decorator that takes arguments", it usually turns out to be something like this:
def decorator(**kwargs):
def _decorator(func):
@wraps(func)
def decorated(*func_args, **func_kwargs):
# do something with kwargs here
return func(*func_args, **func_kwargs)
return decorated
return _decorator
but in this case you'll always have to use your decorator like so:
@decorator() # if you forget () here you'll get an exception
def myfunc():
pass
So I come up with this snippet, IMO this is nicer to read/write and understand:
from functools import wraps, partial
def decorator(func=None, **kwargs):
if func:
@wraps(func)
def decorated(*func_args, **func_kwargs):
if kwargs.get("sayhi"):
print("hi")
return func(*func_args, **func_kwargs)
return decorated
else:
return partial(decorator, **kwargs)
@decorator
def hello(name):
print(f"hello {name}")
@decorator(sayhi=True)
def bye(name):
print(f"bye {name}")
hello("rabbit")
# "hello rabbit"
bye("rabbit")
# hi
# bye rabbit