• 简述python中`functools.wrapper()


    简述python中functools.wrapper()

    首先对于最简单的函数:

    def a():
    	pass
    
    if __name__ == '__main__':
    	print(a.__name__)
    

    输出结果:

    a
    

    然后稍微复杂点:

    def a(func):
    	def wrapper()
    		return func
    
    @a
    def b():
    	pass
    
    if __name__ == '__main__'
    	print(b.__name__)
    

    输出结果:

    a
    

    当加上functools.wrapper时:

    def a(func):
    	@functools.wrapper(func)
    	def wrapper()
    		return func
    
    @a
    def b():
    	pass
    
    if __name__ == '__main__'
    	print(b.__name__)
    

    输出结果:

    b
    

    很明显,通过调用functools.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 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
    class partial:
        """New function with partial application of the given arguments
        and keywords.
        """
    
        __slots__ = "func", "args", "keywords", "__dict__", "__weakref__"
    
        def __new__(cls, func, /, *args, **keywords):
            if not callable(func):
                raise TypeError("the first argument must be callable")
    
            if hasattr(func, "func"):
                args = func.args + args
                keywords = {**func.keywords, **keywords}
                func = func.func
    
            self = super(partial, cls).__new__(cls)
    
            self.func = func
            self.args = args
            self.keywords = keywords
            return self
    
        def __call__(self, /, *args, **keywords):
            keywords = {**self.keywords, **keywords}
            return self.func(*self.args, *args, **keywords)
    
        @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
    
    try:
        from _functools import partial
    except ImportError:
        pass
    
     
    

    上面大致讲的呢,就是通过调用functools.wrappers()来创建了不一样的函数,但是名字却是一样的,且id不一样,功能也可能会有所改变。代码如下:

    import functools
    
    def m(func):
        print(func.__name__)
        print(id(func))
        @functools.wraps(func)
        def wrapper():
            print(wrapper.__name__)
            print(id(wrapper))
        return wrapper
    
    def method1():
        pass
    
    @m
    def method2():
        print(id(method2))
    
    
    if __name__ == '__main__':
        print(method2())
    

    输出:

    method2
    1868266070224
    method2
    1868266070368
    None
    

    综上:调用该函数创建了另一个名字一样的函数,但是内部构造可能会不相同。

  • 相关阅读:
    联合省选 2020 题解
    [省选联考 2020 A 卷] 组合数问题
    【NOI2016】循环之美
    MySQL 之 json 数据操作
    使用Redis和定时实现延时消费
    事务,这次还有不清楚的吗,一次实战坑
    哨兵redis配置
    记录一次shell脚本的使用
    记录一次工作中的SQL优化,临时表和CountDownLatch
    JAVA流读取文件并保存数据
  • 原文地址:https://www.cnblogs.com/JonnyJiang-zh/p/13215415.html
Copyright © 2020-2023  润新知