python 理解functools.wraps

来源:互联网 发布:mac 鼠标右键 编辑:程序博客网 时间:2024/05/16 06:18
先复习下装饰器
# coding=utf-8def logged(func):    def with_logging(*args, **kwargs):        """        哈哈哈,这里是with_logging        :param args:        :param kwargs:        :return:        """        print func.__name__, " was called..."        return func(*args, **kwargs)    return with_logging@loggeddef f(x):    """    哈哈,这是f    :param x:    :return:    """    return x + x * xprint f.__name__print f.__doc__
打印结果是:
(venv) ➜  myApp git:(v1.6_dev) ✗ python 1.pywith_logging        哈哈哈,这里是with_logging        :param args:        :param kwargs:        :return:
理论上这是不对的,因为函数f的信息已经获取不到了。。都被with_logging取代了。
解决方案:
# coding=utf-8import functoolsdef logged(func):    @functools.wraps(func)    def with_logging(*args, **kwargs):        """        哈哈哈,这里是with_logging        :param args:        :param kwargs:        :return:        """        print func.__name__, " was called..."        return func(*args, **kwargs)    return with_logging@loggeddef f(x):    """    哈哈,这是f    :param x:    :return:    """    return x + x * xprint f.__name__print f.__doc__
打印结果是:
(venv) ➜  myApp git:(v1.6_dev) ✗ python 1.pyf    哈哈,这是f    :param x:    :return:
以上是别的教程写的解决方案,突然对这个functools.wraps()有点兴趣,它到底是如何做到的呢?
functools.warp方法就是把被wrapped的函数的属性传递给wrapper的函数。
WRAPPER_ASSIGNMENTS = ('__module__', '__name__', '__doc__')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:        setattr(wrapper, attr, getattr(wrapped, attr))    for attr in updated:        getattr(wrapper, attr).update(getattr(wrapped, attr, {}))    # Return the wrapper so this can be used as a decorator via partial()    return wrapperdef 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)
这个乍一看不是很理解,这个怎么和普通的装饰器不大一样呀。普通的长得像:
def out_wrapper(func):    def in_wrapper(*args, **kwargs):        print ‘in_wrapper’        return func(*args, **kwargs)    return in_wrapper
不过剥丝抽茧,先去除注释:
WRAPPER_ASSIGNMENTS = ('__module__', '__name__', '__doc__')WRAPPER_UPDATES = ('__dict__',)def update_wrapper(wrapper, wrapped, assigned = WRAPPER_ASSIGNMENTS, updated = WRAPPER_UPDATES):    for attr in assigned:        setattr(wrapper, attr, getattr(wrapped, attr))    for attr in updated:        getattr(wrapper, attr).update(getattr(wrapped, attr, {}))    return wrapperdef wraps(wrapped, assigned = WRAPPER_ASSIGNMENTS, updated = WRAPPER_UPDATES):    return partial(update_wrapper, wrapped=wrapped, assigned=assigned, updated=updated)
嗯,看起来简单清爽多了。考虑到偏函数,上面代码可以转成:
WRAPPER_ASSIGNMENTS = ('__module__', '__name__', '__doc__')WRAPPER_UPDATES = ('__dict__',)def update_wrapper(wrapper, wrapped, assigned = WRAPPER_ASSIGNMENTS, updated = WRAPPER_UPDATES):    for attr in assigned:        setattr(wrapper, attr, getattr(wrapped, attr))    for attr in updated:        getattr(wrapper, attr).update(getattr(wrapped, attr, {}))    return wrapperdef wraps(wrapped, assigned = WRAPPER_ASSIGNMENTS, updated = WRAPPER_UPDATES):    def in_wrapper(wrapper, wrapped=wrapped, assigned = WRAPPER_ASSIGNMENTS, updated = WRAPPER_UPDATES):        return update_wrapper(wrapper, wrapped=wrapped, assigned = WRAPPER_ASSIGNMENTS,updated = WRAPPER_UPDATES)    return in_wrapper
一般带参数的装饰器,是三层的,类似:
def wrapper(x):    def out_wrapper(func):        def in_wrapper(*args, **kwargs):            print ‘in_wrapper’            return func(*args, **kwargs)        return in_wrapper    return out_wrapper
虽然长得不一样,但思路就是返回一个能包装函数的函数变量,这帮作者写代码真是骚。
end...