First, the minimum requirement to create a decorator is to implement a function (the decorator) which take a function in parameter.
For instance, you can write:
def my_decorator(f):
def wrapper(*args, **kwargs):
print("f() is called")
return f(*args, **kwargs)
return wrapper
# You can use your decorator w/o parameter:
@my_decorator
def multiply(a, b):
return a * b
You get:
multiply(5, 8)
f() is called
40
As you can see, it works but your decorator take no parameter.
So you need to create a decorator factory:
def my_decorator_factory(message):
def my_decorator(f):
def wrapper(*args, **kwargs):
print("f() is called, saying: " + message)
return f(*args, **kwargs)
return wrapper
return my_decorator
# You can use your decorator factory with a parameter:
@my_decorator_factory(message="hi")
def multiply(a, b):
return a * b
You get:
multiply(5, 8)
f() is called, saying: hi
40
But, you want the ability to call you decorator factory like a decorator (w/o parameter).
So you need to change the signature of your decorator factory to allow the calls like:
my_decorator_factory(message="something") # or
my_decorator_factory(function)
here, you need to check that the first parameter is a function of something else:
import inspect
def my_decorator_factory(message=None):
if inspect.isfunction(message):
return my_decorator_factory(message=None)(message)
else:
def my_decorator(f):
def wrapper(*args, **kwargs):
print("f() is called, saying: " + (message or "(empty)"))
return f(*args, **kwargs)
return wrapper
return my_decorator
@my_decorator_factory
def multiply(a, b):
return a * b
@my_decorator_factory(message='surprise')
def add(a, b):
return a + b
You get:
multiply(5, 8)
f() is called, saying: (empty)
40
add(3, 2)
f() is called, saying: surprise
5
You may consider using Wrapt to implement "good" decorators/decorator factories.