• Python-functools (reduce,偏函数partial,lru_cache)


    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
    reduce()实现代码

        举例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
    partial源代码

         举例 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
          • 不支持清除操作
          • 不支持分布式,是一个单机缓存
        • 适用场景:单机上需要空间换时间的地方,可以用缓存来将计算编程快速查询
    为什么要坚持,想一想当初!
  • 相关阅读:
    mysql 添加自增长ID(序列方式)
    获取本地IP地址信息
    Intellij 快捷键
    java转换汉字为首字母搜索,
    gitee在linux下自动备份
    七牛云续费dns的ssl证书
    shiro 的realm的授权
    realme的shiro简单实现方法
    shiro初探,最简单的认证,通过ini文件。
    git config file
  • 原文地址:https://www.cnblogs.com/JerryZao/p/9551515.html
Copyright © 2020-2023  润新知