函数定义:
WRAPPER_ASSIGNMENTS = ('__module__', '__name__', '__qualname__', '__doc__', '__annotations__') WRAPPER_UPDATES = ('__dict__',) def update_wrapper(wrapper, wrapped, assigned = WRAPPER_ASSIGNMENTS, updated = WRAPPER_UPDATES): """Update a wrapper function to look like the wrapped function wrapper is the function to be updated wrapped is the original function assigned is a tuple naming the attributes assigned directly from the wrapped function to the wrapper function (defaults to functools.WRAPPER_ASSIGNMENTS) updated is a tuple naming the attributes of the wrapper that are updated with the corresponding attribute from the wrapped function (defaults to functools.WRAPPER_UPDATES) """ for attr in assigned: try: value = getattr(wrapped, attr) except AttributeError: pass else: setattr(wrapper, attr, value) for attr in updated: getattr(wrapper, attr).update(getattr(wrapped, attr, {})) # Issue #17482: set __wrapped__ last so we don't inadvertently copy it # from the wrapped function when updating __dict__ wrapper.__wrapped__ = wrapped # Return the wrapper so this can be used as a decorator via partial() return wrapper
def wraps(wrapped, assigned = WRAPPER_ASSIGNMENTS, updated = WRAPPER_UPDATES): """Decorator factory to apply update_wrapper() to a wrapper function Returns a decorator that invokes update_wrapper() with the decorated function as the wrapper argument and the arguments to wraps() as the remaining arguments. Default arguments are as for update_wrapper(). This is a convenience function to simplify applying partial() to update_wrapper(). """ return partial(update_wrapper, wrapped=wrapped, assigned=assigned, updated=updated)
应用示例:
import functools def outer(func): @functools.wraps(func) def inner(*args, **kwargs): print(f"In inner() name={inner.__name__}") func(*args, **kwargs) return inner @outer def add(a, b): print(a + b) add(1, 2)
程序输出:
In inner() name=add #inner.__name__属性被修改为add了
3
函数注解的翻译或理解:
1,update_wrapper函数
修改一个包装函数使它看起来像被包装函数
wrapper是被修改函数
wrapped是源函数
assigned是一个元组,命名从wrapped函数直接赋值给wrapper函数的属性(默认是functools.WRAPPER_ASSIGNMENTS)
updated是一个元组,命名wrapper的属性,这些属性被对应的wrapped函数的属性修改(默认是functools.WRAPPER_UPDATES)
2,wraps函数
装饰器工厂用来应用update_wrapper()到一个wrapper函数(wrapper函数是update_wrapper()的参数wrapper指代的函数,在示例中是inner函数)
返回一个装饰器(一个可调用partial实例对象),该装饰器调用update_wrapper(),update_wrapper()以被装饰的函数(示例中的inner函数)作为wrapper实参,并以wraps()的参数(wrapped,assigned = WRAPPER_ASSIGNMENTS,updated = WRAPPER_UPDATES)作为剩余的实参。wraps()的默认参数(assigned = WRAPPER_ASSIGNMENTS,updated = WRAPPER_UPDATES)和update_wrapper()的默认参数是一样的。
这是一个便利函数用以简化应用partial()于update_wrapper()(update_wrapper函数是实际起作用的函数,wraps和partial只是便利设施)。
关于调用参数:
在wraps函数里返回语句是这样的:
return partial(update_wrapper, wrapped=wrapped,
assigned=assigned, updated=updated)
在partial类里__call__()方法是这样定义的:
def __call__(self, /, *args, **keywords):
keywords = {**self.keywords, **keywords}
return self.func(*self.args, *args, **keywords)
我们注意到生成partial对象时三个参数是用keyword形式传递的,没有位置参数传给partial对象,所以partial对象的args属性为空。当partial对象作为装饰器装饰后跟的函数时,被装饰的函数作为位置参数传给partial对象的__call__()方法,该方法调用update_wrapper函数,因为args属性为空,并且位置参数先于关键字参数匹配,所以该函数传给了update_wrapper函数的wrapper参数。
最后谈谈装饰器涉及到的三个函数的关系,以应用示例里的函数来举例:
outer(func)是装饰器函数,add(a, b)是被装饰函数。对于add函数和inner函数来说,inner函数是包装函数,因为它把add函数包装进了自己的实现,add函数是被包装函数。
经过@functools.wraps(func)装饰后,inner函数的属性更改了,但是函数没有被替换,因为update_wrapper()最后return wrapper。
inner函数扮演了两个角色,一方面它是一个被wraps()返回的partial对象装饰的函数,同时它也是一个包装add()的函数。