笔记-python-functool-@wraps

笔记-python-functool-@wraps

 

1.      wraps

经常看到@wraps装饰器,查阅文档学习一下

在了解它之前,先了解一下partial和updata_wrapper这两个前置技能,因为在wraps中用到了。

 

1.1.    partial

偏函数

源代码:

class partial:

    """New function with partial application of the given arguments

    and keywords.

    """

 

    __slots__ = "func", "args", "keywords", "__dict__", "__weakref__"

 

    def __new__(*args, **keywords):

        if not args:

            raise TypeError("descriptor '__new__' of partial needs an argument")

        if len(args) < 2:

            raise TypeError("type 'partial' takes at least one argument")

        cls, func, *args = args

        if not callable(func):

            raise TypeError("the first argument must be callable")

        args = tuple(args)

 

        if hasattr(func, "func"):

            args = func.args + args

            tmpkw = func.keywords.copy()

            tmpkw.update(keywords)

            keywords = tmpkw

            del tmpkw

            func = func.func

 

        self = super(partial, cls).__new__(cls)

 

        self.func = func

        self.args = args

        self.keywords = keywords

        return self

 

    def __call__(*args, **keywords):

        if not args:

            raise TypeError("descriptor '__call__' of partial needs an argument")

        self, *args = args

        newkeywords = self.keywords.copy()

        newkeywords.update(keywords)

        return self.func(*self.args, *args, **newkeywords)

 

重点关注构造方法和调用方法,可以看出整体的作用等效于构造一个新了函数,但新的函数包括了原函数的参数,多用于简化代码。

实现原理/过程:实际上返回的是一个partial实例(可调用),有三个重要属性,self.func指向传入函数,args和keywords为可变参数;
调用返回的partial对象实质上是执行func()

案例如下:

def u(a,b,*args):

    pass

 

u1 = partial(u,4,5,7)

print('u: {1}{0}u1: {2}{0}u1.func: {3}{0}u1.args: {4}'.format('\n',u,u1,u1.func,u1.args))

 

输出:

u: <function u at 0x000000130F5FB378>
u1: functools.partial(<function u at 0x000000130F5FB378>, 4, 5, 7)
u1.func: <function u at 0x000000130F5FB378>
u1.args: (4, 5, 7)

>>> 

 

1.2.    update_wrapper

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

简单来说,就是把wrapper的相关属性改成和wrapped相同的。返回wrapper

 

1.3.    wraps

回到wraps

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)

核心就一句,实际就是一个修饰器版的update_wrapper,将被修饰的函数(wrapped)

注意这里的写法,wrapped=wrapped,对偏函数而言,该参数写成关键字参数与写成位置参数-wrapped-所带来的结果不一样。

注意,下面这种写法会导致结果反转:

    return partial(update_wrapper, wrapped,

                   assigned=assigned, updated=updated)

 

1.4.    总结

wraps装饰器的作用就是更改函数名称和属性。

当使用装饰器装饰一个函数时,函数本身就已经是一个新的函数;即函数名称或属性产生了变化。所以在装饰器的编写中建议加入wraps确保被装饰的函数不会因装饰器带来异常情况。

posted @ 2019-03-07 00:34  木林森__𣛧  阅读(429)  评论(0)    收藏  举报