python functools常用方法

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功能是相同的。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容