Wrap to alter or enhance the object being wrapped.
@decorate
def func(x):
...
# shorthand for
def func(x):
...
func = decorate(func)
Like:
def trace(func):
def call(*args, **kwargs):
print("Calling", func.__name__)
return func(*args, **kwargs)
return call
@trace
def square(x):
'''
>>> square(3)
9
'''
return x*x
print(square(3))
print("doc string:", square.__doc__) # doc string: None
Function metadata like doc string is now hidden for the wrapped function square.
Best practice:
- DO wrap using
functools.wraps
from functools import wraps
def trace(func):
@wraps(func)
def call(*args, **kwargs):
print("Calling", func.__name__)
return func(*args, **kwargs)
return call
@trace
def square(x):
"""
>>> square(3)
9
"""
return x*x
print(square(3))
print("doc string:", square.__doc__) # doc string: >>> square(3)
9
@wraps() decorator from functools copies function metadata into the replacement function.
When there are multiple decorators, the order might matter:
@decorator1
@decorator2
def func(x):
pass
# shorthand for
def func(x):
...
func = decorator1(decorator2(func))
For example:
class SomeClass(object):
@classmethod # Ok
@trace
def a(cls): # a = classmethod(trace(a))
pass
@trace # Error: @classmethod returns classmethod descriptor object which @trace was not designed to handle
@classmethod
def b(cls): # b = trace(classmethod(b))
pass
A decorator can accept arguments.
@trace("You have called {func.__name__}")
def func():
pass
# shorthand for
def func():
pass
temp = trace("You have called {func.__name__}")
func = temp(func)
# --------------------------------------
from functools import wraps
def trace(message):
def decorate(func): # extra wrap to utilize the message arg
@wraps(func)
def wrapper(*args, **kwargs):
print(message.format(func=func))
return func(*args, **kwargs)
return wrapper
return decorate
The temp above acts as a “decorator factory”. So, we could simplify:
logged = trace("You have called {func.__name__}")
@logged
def func1():
pass
@logged
def func2():
pass
Decorator may not modify the function output, may just do registration.
@eventhandler("BUTTON")
def handle_button(msg):
...
@eventhandler("RESET"):
def handle_reset(msg):
...
_event_handlers = {}
def eventhandler(event):
def register_function(func):
_event_handlers[event] = func
return func
return register_function
Leave a comment