1、functools模块---reduce()
reduce方法:
reduce方法,就是减少
可迭代对象不能为空,初始值没提供就在可迭代对象总去一个元素。
1 def reduce(function, iterable, initializer=None): 2 it = iter(iterable) 3 if initializer is None: 4 value = next(it) 5 else: 6 value = initializer 7 for element in it: 8 value = function(value, element) 9 return value
举例1:
1 from functools import reduce 2 # reduce() 3 print(reduce(lambda a,x:a + x , range(4))) # 6 4 print(reduce(lambda a,x:a + x , range(4), 10)) # 16 5 6 num_l=[1,2,3,100] 7 print(reduce(lambda x,y:x+y,num_l,1)) #可以定义起始值,否则默认起始值为第一个元素 8 print(reduce(lambda x,y:x+y,num_l))
1 print(reduce(lambda a,x:a + x , [])) # 报错,根据定义看,如果没有初始值,那会去迭代对象取第一个值,但是为空,无法next(it)
2、functools ----partial 方法
偏函数:把函数部分的参数固定下来,相当于为部分的参数添加了一个固定的默认值,形成一个新的函数并返回
从 partial 生成的新函数,是对原函数的封装---装饰器
1 def partial(func, *args, **keywords): 2 def newfunc(*fargs, **fkeywords): 3 newkeywords = keywords.copy() 4 newkeywords.update(fkeywords) 5 return func(*args, *fargs, **newkeywords) 6 newfunc.func = func 7 newfunc.args = args 8 newfunc.keywords = keywords 9 return newfunc
举例 1:
1 def partial(func, *args, **keywords): 2 def newfunc(*fargs, **fkeywords): 3 newkeywords = keywords.copy() 4 newkeywords.update(fkeywords) 5 return func(*args, *fargs, **newkeywords) 6 ''' 这是什么意思?''' 7 # newfunc.func = func 8 # print(newfunc.func) # <function add at 0x00000000029DCF28> 9 # newfunc.args = args 10 # print(newfunc.args) # () 11 # newfunc.keywords = keywords 12 # print(newfunc.keywords) # {'y': 5} 13 return newfunc 14 15 def add(x, y): 16 return x + y 17 newadd = partial(add,y=5) 18 # print(newadd(4))
举例2:
1 from functools import partial 2 import inspect 3 4 def add(x:int, y): 5 return x + y 6 7 newadd = partial(add, 4) 8 print(inspect.signature(newadd)) 9 print(newadd(5)) 10 11 # (y) 12 # 9 13 14 newadd = partial(add, y=5) 15 print(inspect.signature(newadd)) 16 print(newadd(4)) 17 18 # (x, *, y=5) # 因为 y=5 是关键字传参,还是可以被更新,所以还是会出现的 19 # 9 20 21 newadd = partial(add, x=4,y=5) 22 print(inspect.signature(newadd)) 23 print(newadd()) 24 25 # (*, x=4, y=5) 26 # 9 27 28 newadd = partial(add,x=4,y=5) 29 print(inspect.signature(newadd)) 30 print(newadd(x=10,y=12)) 31 32 # (*, x=4, y=5) 33 # 22 34 35 36 # newadd = partial(add, x=5) # 报错,虽然x=5被**kwargs收了,但是 4 传入后是frags,最终赋给x,而后面有x=5,重复 37 # print(newadd(4))
举例3:针对 from functools import wraps 的分析
1 '''偏函数作用:把一些函数从多参---变成单参----这些函数就可以当无参装饰器''' 2 3 def update_wrapper(wrapper, 4 wrapped, 5 assigned = WRAPPER_ASSIGNMENTS, 6 updated = WRAPPER_UPDATES): 7 8 def wraps(wrapped, @wraps(fn) 等价 w=wraps(fn)(w) 而wraps(fn)就是一个新函数 9 assigned = WRAPPER_ASSIGNMENTS, wraps函数中可以看到,传入 w,其他的都是固定值,同时返回一个偏函数 10 updated = WRAPPER_UPDATES): 偏函数只有第一个参数没有定,假设生成一个新函数 new() 11 return partial(update_wrapper, wrapped=wrapped, 12 assigned=assigned, updated=updated) 这个new() 只需传入 update_wrapper参数即可,当传入后,update_wrapper 13 只剩wrapped这个参数, w=wraps(fn)(w)这个第二个参数就是w 14 15 from functools import update_wrapper,wraps 16 17 def logger(fn): 18 @wraps(fn) # w=wraps(fn)(w)结构就是一个柯里化的样式 19 def w(*args, **kwargs): 20 ''' this is wrapper function''' 21 print('执行业务功能之前') 22 ret = fn(*args, **kwargs) 23 print('执行业务功能之后') 24 return ret 25 # update_wrapper(w, fn) 26 return w 27 28 @logger # add = logger(add) ----> add = wrapper 29 def add(x, y): 30 ''' this is add function''' # 这个只能放在第一行才能打印!!!!! 31 return x + y
3、lru_cache
functools.lru_cache(maxsize=128, typed=False)
-
-
- Least-recently-used 装饰器,lru 最近最少使用。cache缓存
- 如果maxsize 设置为 None,则禁用LRU 功能,并且缓存可以无限制增长,当maxsize是二的幂次时,LRU 功能执行的最好
- 如果typed设置为True,则不同类型的函数将单独缓存,例如 f(3) 和 f(3.0)将被视为具有不同结果的不同调用。
-
举例 1:
1 from functools import lru_cache 2 import time 3 4 @lru_cache() # 带参的装饰器 5 def add(x=[], y=1): 6 x.append(y) 7 return x 8 9 print(add()) 10 print(add()) # 第二次执行,是直接找上次的结果,并不会在追加,如果不加缓存,就会继续追加 11 12 print(add([0])) # 不可hash,以为@lru_cache 原码影响。
lru_cache 源码分析:
1 class _HashedSeq(list): 2 """ This class guarantees that hash() will be called no more than once 3 per element. This is important because the lru_cache() will hash 4 the key multiple times on a cache miss. 5 6 """ 7 8 __slots__ = 'hashvalue' 9 10 def __init__(self, tup, hash=hash): 11 self[:] = tup 12 self.hashvalue = hash(tup) 13 14 def __hash__(self): 15 return self.hashvalue 16 17 def _make_key(args, kwds, typed, 18 kwd_mark = (object(),), 19 fasttypes = {int, str, frozenset, type(None)}, 20 tuple=tuple, type=type, len=len): 21 """Make a cache key from optionally typed positional and keyword arguments 22 23 The key is constructed in a way that is flat as possible rather than 24 as a nested structure that would take more memory. 25 26 If there is only a single argument and its data type is known to cache 27 its hash value, then that argument is returned without a wrapper. This 28 saves space and improves lookup speed. 29 30 """ 31 # All of code below relies on kwds preserving the order input by the user. 32 # Formerly, we sorted() the kwds before looping. The new way is *much* 33 # faster; however, it means that f(x=1, y=2) will now be treated as a 34 # distinct call from f(y=2, x=1) which will be cached separately. 35 key = args 36 if kwds: 37 key += kwd_mark 38 for item in kwds.items(): 39 key += item 40 if typed: 41 key += tuple(type(v) for v in args) 42 if kwds: 43 key += tuple(type(v) for v in kwds.values()) 44 elif len(key) == 1 and type(key[0]) in fasttypes: 45 return key[0] 46 return _HashedSeq(key) 47 48 def lru_cache(maxsize=128, typed=False): 49 50 # 从原码可以看出,args 只能是元组,因为最终返回 _HashedSeq(key),而事实上,class _HashedSeq(list): 是元组继承而来,def __init__(self, tup, hash=hash): 51 # 而且必须可hash,所以list,set,dict都不能, 52 # 对传入的参数,位置参数赋值给key,之后会添加一个 object() 形成一个新的 tuple,再将关键字参数,形成的字典,以元组形式与之前的 53 # 的元组相加,形成新的元组,最后加一个type 即元素的类型。 54 # 如下: 55 # from functools import _make_key 56 # key = _make_key((1,2,3),{'a':1},False) 57 # print(key) # [1, 2, 3, <object object at 0x000000000019E160>, 'a', 1] 58 # 59 # print(hash(key)) # -147545184494388729
举例 2:
1 from functools import lru_cache 2 import time 3 4 @lru_cache() 5 def add(x=4, y=5): 6 time.sleep(3) 7 return x + y 8 9 print(1,add(4,5)) 10 print(2,add(4)) 11 print(3,add(y=5)) 12 print(4,add(x=4,y=5)) 13 print(5,add(y=5,x=4)) 14 print(6,add(4.0,5)) 15 # 如上,每个都要算 3 秒 16 17 @lru_cache() 18 def add(x=[], y=5): 19 time.sleep(3) 20 x.append(1) 21 return x 22 23 print(add([1])) # TypeError: unhashable type: 'list'
总结:lru_cache装饰器应用
-
-
- 使用前提:
- 同样的函数参数一定得到同样的结果
- 函数执行时间很长,且要多次执行
- 本质:函数调用的参数 ==> 返回值
- 缺点:
- 不支持缓存过期,key无法过期,shixiao
- 不支持清除操作
- 不支持分布式,是一个单机缓存
- 适用场景:单机上需要空间换时间的地方,可以用缓存来将计算编程快速查询
- 使用前提:
-