美文网首页程序员
python functools常用方法

python functools常用方法

作者: 第八共同体 | 来源:发表于2017-08-03 12:06 被阅读0次

    functools模块用于高级函数:作用于或返回其他函数的函数,一般来说,任何可调用对象都可以作为这个模块的用途来处理。

    1、lru_cache

    @functools.lru_cache(maxsize=128, typed=False)
    使用functools模块的lur_cache装饰器,可以缓存最多 maxsize 个此函数的调用结果,从而提高程序执行的效率,特别适合于耗时的函数。参数maxsize为最多缓存的次数,如果为None,则无限制,设置为2n时,性能最佳;如果 typed=True(注意,在 functools32 中没有此参数),则不同参数类型的调用将分别缓存,例如 f(3) 和 f(3.0)。

    被 lru_cache 装饰的函数会有 cache_clear 和 cache_info 两个方法,分别用于清除缓存和查看缓存信息。

    from functools import lru_cache
    
    @lru_cache(maxsize=32)
    def fib(n):
        print('calling the fib function....')
        if n < 2:
            return n
        return fib(n-1) + fib(n-2)
    
    
    
    if __name__ == '__main__':
        print(list([fib(n) for n in range(16)]))
        [print(func) for func in dir(fib) if not func.startswith('_')]
        print(fib.cache_info())
        print('------------')
        print([fib(n) for n in range(16)])
    
    

    运行结果:

    image.png

    如果把第二次调用的参数改为大于16的数,注意看结果

    image.png

    可以看出,在已经缓存的数据中,不会进行重复调用函数,但是,未被缓存的数据将再次调用函数。

    2、partial

    functools.partial(func, *args, **keywords)
    大致如下:

    def partial(func, *args, **keywords):
        def newfunc(*fargs, **fkeywords):
            newkeywords = keywords.copy()
            newkeywords.update(fkeywords)
            return func(*(args + fargs), **newkeywords)
        newfunc.func = func
        newfunc.args = args
        newfunc.keywords = keywords
        return newfunc
    
    image.png

    可以看出函数partial的返回值是一个partial类,该类有三个只读属性

    partial.func:可调用的对象或函数
    partial.args:最左边的位置参数将被预先提交给partial对象调用所提供的位置参数
    partial.keywords:当调用partial对象时,将提供关键字参数.

    3.reduce

    functools.reduce(function, iterable[, initializer])
    大致如下:

    def reduce(function, iterable, initializer=None):
        it = iter(iterable)
        if initializer is None:
            value = next(it)
        else:
            value = initializer
        for element in it:
            value = function(value, element)
        return value
    
    image.png

    4.update_wrapper

    functools.update_wrapper(wrapper, wrapped, assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES)
    找不到很好的解释该函数的材料,于是,看了一下源码:

    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
    

    5.wraps

    @functools.wraps(wrapped, assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES)
    是不是觉得这坨跟update_wrapper很像,是的,他其实调用了update_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)
    

    怎么样,使用partial固定住三个关键字参数以后,开始使用update_wrapper函数调用,只需传入wrapper参数即可。具体例子说明会更清晰:

    from functools import update_wrapper,wraps
    
    
    def my_decorator1(f):
         def wrapper(*args, **kwds):
             print('Calling decorated function')
             return f(*args, **kwds)
         return update_wrapper(wrapper=wrapper ,wrapped=f)
    
    def my_decorator2(f):
        @wraps(f)
        def wrapper(*args, **kwds):
            print('Calling decorated function')
            return f(*args, **kwds)
        return wrapper
    
    
    @my_decorator1
    def example():
        """Docstring"""
        print('Called example function')
    
    if __name__ == '__main__':
        # process result:
        # Calling decorated function
        # Called example function
        example()
        # add wraps decorator reuslt is :example Docstring __main__
        # print example.__name__, example.__doc__, example.__module__
        # not add wraps decorator reuslt is :wrapper None __main__
        print('example_name: %s, example_doc: %s, example_module: %s '%(example.__name__, example.__doc__, example.__module__))                                                                                                                     
    

    上面的例子中,wraps装饰器的参数是my_decorator2装饰器的参数,本例中也是example函数,在wraps调用partial函数时,wrapped参数值就赋值为example函数。我们知道wraps函数的返会结果是可调用partial对象,该对象的func属性时update_wrapper.所以my_decorator2的功能实际上和my_decorator1功能是相同的。

    相关文章

      网友评论

        本文标题:python functools常用方法

        本文链接:https://www.haomeiwen.com/subject/ycaulxtx.html