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)])
运行结果:
如果把第二次调用的参数改为大于16的数,注意看结果
可以看出,在已经缓存的数据中,不会进行重复调用函数,但是,未被缓存的数据将再次调用函数。
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
可以看出函数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
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功能是相同的。