reduce
- reduce顾名思义就是减少
- reduce(function,seq[,initial])
- 可迭代对象不能为空;初始值没有就从可迭代对象中取一个元素
from functools import reduce
num=[1,2,3,4,5]
print(sum(num))
print(reduce(lambda val,x:val + x),num)#相当于sum函数
print(reduce(lambda val,x:val * x),num)#相当于num的阶乘
partial–偏函数
- 把部分参数固定下来,相当于为部分参数添加了一个固定的默认值,形成一个新的函数并返回
- 从partial生成返回的新函数,是对原函数的封装
from functools import partial
def add(x,y):
return x + y
newadd=partial(add,4)
print(newadd(5))
print(newadd(5,y=10))#TypeError: add() got multiple values for argument 'y'
print(newadd(x=5,y=10))#add() got multiple values for argument 'x'
分析:
partial函数相当于提前为部分参数添加默认值,所以当调用partial函数给定一个位置参数传参时(partial(add,4)),这个值会给第一个参数(x),返回的新函数为newadd(x=4,y)
partial函数的本质(伪代码)
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
lru_cache
@functools.lrucache(maxsize=128,typed=False)
- Least-recently-used装饰器,lru最近最少使用,cache缓存;
- 如果maxsize设置为None,则禁用lru功能,并且缓存可以无限制增长,当maxsize是2的幂时,LRU功能执行最好;
- 如果typed设置为True,则不同类型的函数参数将单独缓存,例如:fn(5)和fn(5.0),将被视为不同结果的不同调用。
举例:
from functools import lru_cache
import time
@lru_cache()
def add(x,y):
time.sleep(2)
return x + y
add(4,5)
add(4.0,5)
add(5,4)
add(4,y=5)
总结:
- lru_cache是通过字典把被装饰函数的调用之和返回值储存起来;
- 使用前提:同样的参数一定得到同样的结果,函数执行时间长且需要执行多次;
- 本质是函数调用的参数=>返回值;
- 缺点:不支持缓存过期,key无法过期,清除;不支持分布式,单机缓存
自定义一个cache函数,实现过期清除key的功能
import inspect
from functools import wraps
import time
import datetime
def cache(fn):
local_cache = {} # 对不同函数名是不同的cache
@wraps(fn)
def wrapper(*args, **kwargs):
def _clear_expire():
# 清除缓存:注意:在对字典迭代的时候,是不能删除的!
expire_keys = []
# for k, v in local_cache.items(): # {(('x', 4), ('y', 5)): (9, 1535638337.355427)}
# ret, stamp = v
# 等价
for k,(_,stamp) in local_cache.items():
if datetime.datetime.now().timestamp() - stamp > 5:
expire_keys.append(k)
for k in expire_keys:
local_cache.pop(k)
# 每次都要遍历,最好用线程,用的时候清除
def _make_key(args,kwargs):
sig = inspect.signature(fn) # 获取签名 (x=4, y=5)
params = sig.parameters # 获取参数字典,有序的 OrderedDict([('x', <Parameter "x=4">), ('y', <Parameter "y=5">)])
values = list(params.values()) # 有序的 [<Parameter "x=4">, <Parameter "y=5">]
keys = list(params.keys()) # ['x', 'y']
param_dic = {} # c参数字典
# 位置传参
for k, v in enumerate(args):
param_dic[keys[k]] = v
param_dic.update(zip(params.keys(), args)) # 二元组,放到字典中,形成 k-v对
# 关键字传参
param_dic.update(kwargs) # 关键字参数,放到字典中
# 缺省值
for k, v in params.items(): # OrderedDict([('x', <Parameter "x=4">), ('y', <Parameter "y=5">)])
if k not in param_dic:
param_dic[k] = v.default
key = tuple(sorted(param_dic.items())) # sorted生成列表,不能做key,所以tuple一下(key必须是可hash的,因为要用字典) (('x', 4), ('y', 5))
return key
_clear_expire()
key = _make_key(args,kwargs)
if key not in local_cache.keys():
local_cache[key] = fn(*args, **kwargs), datetime.datetime.now().timestamp() # 放到二元组中,记录的是缓存的时间
# 形成一个 {key:stamp}的字典
#如果说到点清除,最好写结束时间,到点清除,记录一个未来时间
print(local_cache) # {(('x', 4), ('y', 5)): (9, 1535638337.355427)}
# print(local_cache[key])
return local_cache[key]
return wrapper
@cache
def add(x,y):
return x+y
add(4,5)
time.sleep(5)
add(4,y=5)
time.sleep(5)
add(x=4,y=5)