wraps actually have no practical use. They are used to solve the problem of changes in the properties of the function pointed to by the original function name caused by the decorator;
The decorator has decorated the function func. At this time, func does not point to the real func, but Points to the decorated function in the decorator
import sys debug_log = sys.stderr def trace(func): if debug_log: def callf(*args, **kwargs): """A wrapper function.""" debug_log.write('Calling function: {}\n'.format(func.__name__)) res = func(*args, **kwargs) debug_log.write('Return value: {}\n'.format(res)) return res return callf else: return func @trace def square(x): """Calculate the square of the given number.""" return x * x
The square here actually points to calls. You can use help(square) or square.__name__ to check it out.
The decorated function of square is actually another function (function attributes such as function name will change)
If you use wraps for decoration
def trace(func): if debug_log: @functools.wraps(func) def callf(*args, **kwargs): """A wrapper function.""" debug_log.write('Calling function: {}\n'.format(func.__name__)) res = func(*args, **kwargs) debug_log.write('Return value: {}\n'.format(res)) return res return callf else: return func
At this time, the attributes of square decorated with trace will not change Now, you can help(square) to see
Reason: We translate the wraps decoration code as follows, and its equivalent is:
def trace(func): if debug_log: def _callf(*args, **kwargs): """A wrapper function.""" debug_log.write('Calling function: {}\n'.format(func.__name__)) res = func(*args, **kwargs) debug_log.write('Return value: {}\n'.format(res)) return res callf = functools.update_wrapper(_callf, wrapped = func,assigned = functools.WRAPPER_ASSIGNMENTS,updated = functools.WRAPPER_UPDATES) return callf else: return func
update_wrapper does a very simple job, which is a function object represented by the parameter wrapped (for example: square ) some attributes (such as: __name__, __doc__) override these corresponding attributes of the function object represented by the parameter wrapper (for example: callf, where callf simply calls the square function, so it can be said that callf is a wrapper function of square).
Therefore, after using the wraps decorator to "decorate" callf in this example, the __doc__, __name__ and other attributes of callf are exactly the same as those of the function square to be "decorated" by trace.