functools模块
functools模块
reduce
- functools.reduce(function , iterable[,initial])
- 就是减少的意思
- 初始值没有提供就可在可迭代对象中取一个
from functools import reduce
s = sum(range(10))
print(s)
s = reduce(lambda x:x ,range(10))
print(s)#TypeError: <lambda>() takes 1 positional argument but 2 were given
从上面的异常推断lambda应该2个参数
s = reduce(lambda x,y: print(x,y), range(10))
print(s)
返回结果如下
0 1
None 2
None 3
None 4
None 5
None 6
None 7
None 8
None 9
None
上一次lambda函数返回值会成为下一次的x
from functools import reduce
s = sum
s = sum(range(10))
print(s)
s = reduce(lambda x,y: x + y, range(10), 100)
print(s)
sum只能求和,reduce能做更加复杂的迭代计算。
#阶乘
mm = reduce(lambda x,y:x*y,range(1,5))
print(mm)
partial
偏函数
- 把函数部分参数固定下来,相当于为部分的参数添加了固定的默认值,形成一个新函数,并返回这个新函数
- 这个新函数是对原函数的封装
from functools import partial
def add(x, y):
return x + y
newadd = partial(add, y=5)
print(newadd(4))
print(newadd(4, y=15))
print(newadd(x=4))
print(newadd(4, 6)) # 可以吗 不可以,y重复定义赋值
print(newadd(y=6, x=4))
import inspect
print(inspect.signature(newadd))
from functools import partial
def add(x, y, *args):
return x + y + sum(args)
newadd = partial(add, 1, 2, 3, 4, 5)
print(newadd())
print(newadd(1))
print(newadd(1, 2))
print(newadd(x=1)) # #不可以
print(newadd(x=1, y=2)) #
import inspect
print(inspect.signature(newadd))
(*args)
parital本质
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
def add(x, y):
return x + y
foo = partial(add, 4)
foo(5)
尝试分析functools.wraps的实现。类别下面的实现
from functools import partial, wraps
import inspect
def add(a, b, c, d):
return a + b + c + d
newadd = partial(add, b=2, c=3, d=4)
print(inspect.signature(newadd))
lru_cache
@functools.lru_cache(maxsize=128,type=False)
- lru即Least-recently-used,最近最少使用。cache缓存
- 如果maxsize设置问None,则禁用LRU功能,并且缓存可以无限增长。当maxsize是二的幂时,LRU功能执行的最好
- 如果typed设置为True,则不同类型的函数参数将单独缓存。例如,f(3)和f(3.0)将被视为具有不同结果的不同调用
- Python3.8简化了调用,可以使用
@functools.lru_cache
def add():
pass
# 等价于
@functools.lru_cache(128)
def add():
pass
from functools import lru_cache
import time
@lru_cache()
def add(x, y=5):
print('-' * 30)
time.sleep(3)
return x + y
print(1, add(4, 5))
print(2, add(4, 5)) #第二次调用速度很快,没有执行add函数
print(3, add(x=4, y=5))
print(4, add(y=5, x=4))
print(5, add(4.0, 5))
print(6, add(4))
lru_cache本质
- 内部使用了一个字典
- key是由make_key函数构造出来
from functools import _make_key
print(_make_key((4, 5), {}, False))
[4, 5]
print(_make_key((4, 5), {}, True))
[4, 5, <class 'int'>, <class 'int'>]
print(_make_key((4,), {'y':5}, False))
[4, <object object at 0x000001DD8FE84F20>, 'y', 5]
print(_make_key((), {'x':4, 'y':5}, False))
[<object object at 0x000001DD8FE84F20>, 'x', 4, 'y', 5]
print(_make_key((), {'y':5, 'x':4}, False))
[<object object at 0x000001DD8FE84F20>, 'y', 5, 'x', 4]
应用
#斐波那契数列
from functools import lru_cache
@lru_cache()
def fib(n):
return 1 if n<3 else fib(n-1) + fib(n-2)
print(fib(101))
总结:
lru_cache装饰器应用
- 使用前提
- 同样的函数参数一定得到同样的结果,至少是一段时间内,同样输入得到同样结果
- 计算代价高,函数执行很长
- 需要多次执行,每一次计算代价高
- 本质是建立函数调用的函数到返回值的映射
- 缺点
- 不支持缓存过期,key无法过期、失效
- 不支持清除操作
- 不支持分布式,是一个单机的缓存
- lru_cache适用场景,单机上需要空间换时间的地方,可以用缓存来将计算变成快速的查询
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 J.のblog!
评论