python 理解functools.wraps

版权声明:随意转载。 https://blog.csdn.net/xihuanqiqi/article/details/74929418
先复习下装饰器
# coding=utf-8

def 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

@logged
def f(x):
    """
    哈哈,这是f
    :param x:
    :return:
    """
    return x + x * x

print f.__name__
print f.__doc__
打印结果是:
(venv) ➜  myApp git:(v1.6_dev) ✗ python 1.py
with_logging

        哈哈哈,这里是with_logging
        :param args:
        :param kwargs:
        :return:
理论上这是不对的,因为函数f的信息已经获取不到了。。都被with_logging取代了。
解决方案:
# coding=utf-8
import functools

def 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

@logged
def f(x):
    """
    哈哈,这是f
    :param x:
    :return:
    """
    return x + x * x

print f.__name__
print f.__doc__
打印结果是:
(venv) ➜  myApp git:(v1.6_dev) ✗ python 1.py
f

    哈哈,这是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 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)
这个乍一看不是很理解,这个怎么和普通的装饰器不大一样呀。普通的长得像:
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 wrapper

def 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 wrapper

def 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...

猜你喜欢

转载自blog.csdn.net/xihuanqiqi/article/details/74929418