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