笔记-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)

    @recursive_repr()

    def __repr__(self):

        qualname = type(self).__qualname__

        args = [repr(self.func)]

        args.extend(repr(x) for x in self.args)

        args.extend(f"{k}={v!r}" for (k, v) in self.keywords.items())

        if type(self).__module__ == "functools":

            return f"functools.{qualname}({', '.join(args)})"

        return f"{qualname}({', '.join(args)})"

    def __reduce__(self):

        return type(self), (self.func,), (self.func, self.args,

               self.keywords or None, self.__dict__ or None)

    def __setstate__(self, state):

        if not isinstance(state, tuple):

            raise TypeError("argument to __setstate__ must be a tuple")

        if len(state) != 4:

            raise TypeError(f"expected 4 items in state, got {len(state)}")

        func, args, kwds, namespace = state

        if (not callable(func) or not isinstance(args, tuple) or

           (kwds is not None and not isinstance(kwds, dict)) or

           (namespace is not None and not isinstance(namespace, dict))):

            raise TypeError("invalid partial state")

        args = tuple(args) # just in case it's a subclass

        if kwds is None:

            kwds = {}

        elif type(kwds) is not dict: # XXX does it need to be *exactly* dict?

            kwds = dict(kwds)

        if namespace is None:

            namespace = {}

        self.__dict__ = namespace

        self.func = func

        self.args = args

        self.keywords = kwds

用于构造一个新了函数,但新的函数包括了原函数的参数,目前看来多用于简化代码。

def u(a,b,*args):

    pass

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

print('u:{}\nu1:{}\nu1.func{}\nu1.args{}'.format(u,u1,u1.func,u1.args))

def ad(*args):

    return args[0] + args[1]

ad2 = partial(ad,3,4,5)

输出:

u:<function u at 0x000000CF4FA02E18>

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

u1.func<function u at 0x000000CF4FA02E18>

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不一样。

1.4.    总结

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

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

猜你喜欢

转载自www.cnblogs.com/wodeboke-y/p/10487064.html