Python模块itertools, functools
〇、前言
本系列(指Python Moudules系列)每篇介绍一个或多个内容没有特别多模块
模块介绍依赖官方文档(https://docs.python.org/zh-cn/3.8/library/index.html)或其他第三方官方文档,主要是对其内容的补充和拓展
内容大部分来源官方文档,少部分来源网络。太过深奥的内容会只写出不介绍,或省略,或省略掉太过深奥的部分(例如省略部分参数),或通过补充介绍(补充介绍的前面通常会有参考网址)
注意内容非常依赖编号。内容中类的展示方式是"编号<数字>."表示类的构造方法(__init__),"编号<数字>.<数字>"表示类的方法或属性,方法一般是实例方法,其他方法会在前面备注,属性一般是实例属性,类属性会在后面备注。如果构造方法无需传参会直接展示类名
使用Python版本3.8,但也会补充高版本的修改和添加内容。系统Windows 7
系列WPS笔记及其他内容的百度网盘链接:
https://pan.baidu.com/s/1fTwjyoM81_OOAccPNhGE9Q?pwd=h3fg
颜色代表含义:
淡灰色:注释,一般前面有#
绿色:示例
橙色:拓展
紫色:示例中的input用户输入内容
红色、蓝色:突出或装饰文字作用
部分内容可能不严谨或者错误,欢迎指出

一、itertools——为高效循环而创建迭代器的函数
(1)、概念
itertools内置模块实现一系列迭代器,标准化了一个快速、高效利用内存的核心工具集,这些工具本身或组合一起形成了“迭代器代数”,这使得在纯Python中有可能创建简洁又高效的专用工具
这些内置工具同时也能很好地与operator模块中的高效函数配合使用
(2)、函数
函数均创建并返回迭代器。有些迭代器不限制输出流的长度(例如count()),这些迭代器只应在能截断输出流的函数或循环中使用
# 官方文档中的“大致相当于:”中代码不会展示
# 事实上这些函数都是类的实例化或方法
1. 无穷迭代器
# 无穷指迭代器迭代的元素是无穷的
1.1. count(start=0, step=1)
创建一个迭代器,它从start值开始,按步长step返回均匀间隔的值
# 当对浮点数计数时,替换为乘法代码有时精度会更好,例如:(start + step * i for i in count())
示例:count(5, 2) --> 5, 7, 9, 11, 13, ...
1.2. cycle(iterable)
创建一个迭代器,返回iterable中所有元素并保存一个副本。当取完iterable中的元素时,返回副本中的所有元素。无限循环
# 注意,该函数可能需要相当大的辅助空间(取决于iterable的长度)
示例:cycle([1, 'A', True, None]) --> 1, A, True, None, 1, A, True, None, ...
1.3. repeat(object[, times])
创建一个迭代器,重复times次object。如果times未指定则无限重复
# 常用在map或zip中提供一个常量流
示例:
repeat(5) --> 5, 5, 5, 5, 5, ...
repeat(5, 5) -> 5, 5, 5, 5, 5
list(map(pow, range(5), repeat(2))) --> [0, 1, 4, 9, 16]
2. 根据最短输入序列长度停止的迭代器
2.1. accumulate(iterable[, func, *, initial=None])
创建一个迭代器,返回iterable中元素的累积汇总值或其他双目运算函数的累积结果值
func——应为带有两个参数的函数(双目运算函数)。iterable 中的元素可以是任何能被func接受为参数的任意类型
initial——累加的初始值,这会使输出比输入的iterable多一个元素
示例:
accumulate([1, 1, 2, 2, 3]) --> 1, 2, 4, 6, 9
accumulate(['a', 'b', 'c', 'd', 'e']) --> a, ab, abc, abcd, abcde
accumulate([1, 1, 2, 2, 3], initial=100) --> 100, 101, 102, 104, 106, 109
accumulate([1, 1, 2, 2, 3], func=lambda a, b: a*b) --> 1, 1, 2, 4, 12
2.2. chain(*iterables)
创建一个迭代器,依次返回iterables中的所有元素,直到耗尽。可将多个序列处理为单个序列
示例:
chain('aaa', 'bbb', 'abc') --> a, a, a, b, b, b, a, b, c
chain([1, 2], (3, 4), {5, 6}) --> 1, 2, 3, 4, 5, 6
2.3. chain.from_iterable(iterable)
创建一个迭代器,依次返回iterable(注意是单一的iterable)中的所有元素,直到耗尽。参数是延迟计算的
示例:
chain.from_iterable(['a', 'bb', 'ccc']) --> a, b, b, c, c, c
补充:延迟计算/延迟求值/惰性求值(Lazy Evaluation)
# 摘抄自百度百科
表达式不在它被绑定到变量之后就立即求值,而是在该值被取用的时候求值
示例:
2.4. compress(data, selectors)
创建一个迭代器,返回data中元素对应的selectors中元素布尔值为True的元素。迭代器再两者较短长度处停止
示例:
compress('ABCDEFG', [1, 1, 0, 1, 0, 1, 0]) --> A, B, D, F
compress(count(), cycle([1, 0]) --> 0, 2, 4, 6, ...
2.5. dropwhile(predicate, iterable)
创建一个迭代器,并将iterable中的元素作为参数传递给predicate进行判断,在首次判断为False后返回剩下的元素
示例:
dropwhile(lambda a: a < 5, [1, 2, 3, 6, 1, 0, -100]) --> 6, 1, 0, -100
2.6. takewhile(predicate, iterable)
创建一个迭代器,并将iterable中的元素作为参数传递给predicate进行判断,在首次判断为False后停止迭代
示例:
takewhile(lambda a: a < 5, [1, 2, 3, 6, 1, 0, -100]) --> 1, 2, 3
2.7. filterfalse(predicate, iterable)
创建一个迭代器,并将iterable中的所有元素作为参数传递给predicate进行判断,只返回判断结果是False的元素。如果predicate是None,则返回predicate中布尔值为False的元素
# 相当于内置filter()函数的反向函数
示例:
filterfalse(lambda a: a < 5, [1, 2, 3, 6, 1, 0, -100]) --> 6
filter(lambda a: a < 5, [1, 2, 3, 6, 1, 0, -100]) --> 1, 2, 3, 1, 0, -100
2.8. groupby(iterable, key=None)
创建一个迭代器,返回iterable中连续键及对应组:(键, 组)
key——计算元素键值的函数,元素会作为参数传递给此函数。key默认是恒等函数(identity function,返回值等于传入值的函数)
# 一般iterable需要以key作为依据排序:sorted(iterable, key=key)
# 组本身也是一个迭代器,当groupby()对象向后迭代时,前一个组会消失。因此如果后面需要组,可将它保存为列表
示例:
运行结果:
[('a', ['a', 'a', 'a']), ('b', ['b']), ('c', ['c', 'c']), ('a', ['a'])]
[0, 1] [[2, 2], [1, 3, 1, 3]]
2.9. islice(iterable, stop)/islice(iterable, start, stop[, step])
创建一个迭代器,类似于切片地从iterable返回选定的元素
将start, stop, step设置为None相当在切片中留空
与普通切片不同的是不支持将start, stop, step设置为负值
示例:
islice('ABCDEFG', 2, None) --> C, D, E, F, G
islice('ABCDEFG', 0, None, 2) --> A, C, E, G
islice(range(0, 100, 3), 0, None, 3) --> 0, 9, 18, 27, 36, 45, 54, 63, 72, 81, 90, 99
2.10. starmap(function, iterable)
创建一个迭代器,类似于内置函数map(),不过iterable中的每一项会被解包再作为函数参数
# 例如可迭代对象[(1,2), (3, 4)]会转化为等价于[function(1, 2), function(3, 4)]的调用
示例:
starmap(lambda a, b: a + b, [(1, 2), (2, 3), (3, 4)]) --> 3, 5, 7
map(lambda a, b: a + b, (1, 2, 3), (2, 3, 4)) --> 3, 5, 7
2.11. tee(iterable, n=2)
从一个可迭代对象中返回n个独立的迭代器。调用tee()创建迭代器后,原有的iterable不应再被使用,否则会使tee向后迭代或迭代元素多出创建时的元素数量
示例:
a, b = tee([1, 2, 3, 4, 5])
next(a) --> 1
list(b) --> [1, 2, 3, 4, 5]
# 迭代器a和b互不影响
2.12. zip_longest(*iterables, fillvalue=None)
创建一个迭代器,从每个可迭代对象(iterables中的每个元素)中收集元素并将它们组成一个元组返回。如果可迭代对象的长度不相等,将根据fillvalue填充缺失值。迭代持续到耗光最长的可迭代对象
# 相当于内置函数zip()以长的为基准的打包
示例:
l1 = ['a', 'b', 'c', 'd', 'e']
l2 = ['1', '2', '3']
zip_longest(l1, l2, fillvalue='-') --> ('a', '1'), ('b', '2'), ('c', '3'), ('d', '-'), ('e', '-')
zip(l1, l2) --> ('a', '1'), ('b', '2'), ('c', '3')
3. 排列组合迭代器
# 元素值相同位置不同会被认为是不同的
3.1. product(*iterables, repeat=1)
创建一个迭代器,返回iterables的笛卡儿积
repeat——计算可迭代对象自身的笛卡儿积可将此参数设定要重复的次数。如product(A, repeat=4)等价于product(A, A, A, A)
# 大致相当于生成器表达式中的嵌套循环。如product(A, B)和((x,y) for x in A for y in B)返回的结果相同
示例:
product('AB', 'CD') --> ('A', 'C'), ('A', 'D'), ('B', 'C'), ('B', 'D')
product('AB', repeat=2) --> ('A', 'A'), ('A', 'B'), ('B', 'A'), ('B', 'B')
product('ab', 'cd', 'ef') --> ('a', 'c', 'e'), ('a', 'c', 'f'), ('a', 'd', 'e'), ('a', 'd', 'f'), ('b', 'c', 'e'), ('b', 'c', 'f'), ('b', 'd', 'e'), ('b', 'd', 'f')
补充:笛卡尔积(Cartesian product)
# 摘编自百度百科
两个集合X和Y的笛卡儿积,又称直积,表示为X×Y,是第一元素是X的成员而第二元素是Y成员的所有可能有序对的其中一个成员
3.2. permutations(iterable, r=None)
创建一个迭代器,返回由iterable元素生成长度为r的排列,无重复元素
r——如果r未指定或为None,则r默认设置为iterable的长度(全排列)
示例:
permutations(['A', 'B']) --> ('A', 'B'), ('B', 'A')
permutations(['A', 'B', 'C'], r=2) --> ('A', 'B'), ('A', 'C'), ('B', 'A'), ('B', 'C'), ('C', 'A'), ('C', 'B')
3.3. combinations(iterable, r)
创建一个迭代器,返回由iterable元素生成长度为r的组合,无重复元素
示例:
combinations(['A', 'B'], r=2) --> ('A', 'B')
combinations(['A', 'B', 'C'], r=2) --> ('A', 'B'), ('A', 'C'), ('B', 'C')
3.4. combinations_with_replacement(iterable, r)
创建一个迭代器,返回由iterable元素生成长度为r的组合,有重复元素
示例:
combinations_with_replacement(['A', 'B'], r=2) --> ('A', 'A'), ('A', 'B'), ('B', 'B')
combinations_with_replacement(['A', 'B', 'C'], r=2) --> ('A', 'A'), ('A', 'B'), ('A', 'C'), ('B', 'B'), ('B', 'C'), ('C', 'C')
补充:itertools配方
itertools 配方是使用itertools作为基础构件来创建扩展的工具集
可以安装more-itertools模块获取许多itertools配方
二、functools——高阶函数和可调用对象上的操作
(1)、概念
functools内置模块应用于高阶函数(参数或/和返回值为其他函数的函数)。通常此模块的功能适用于所有可调用对象
# 只会简述部分函数,具体见官方文档
(2)、部分函数/装饰器
1. @functools.cached_property(func)
将一个类方法转换为特征属性并一次性计算该特征属性的值(实测在获取属性的时计算),然后将其缓存为实例生命周期内的普通属性。缓存的值可通过删除该属性来清空
# 此装饰器要求每个实例上的__dict__是可变的映射
示例:
运行结果:
data_num: 3
cache_data_num: 3
data_num: 6
cache_data_num: 3
cache_data_num: 6
2. @functools.lru_cache(maxsize=128, typed=False)
为函数提供缓存功能的装饰器,缓存maxsize组传入参数,在下次同样参数调用时直接返回同样的结果
# 由于使用了字典存储缓存,所以该函数的固定参数和关键字参数必须是可哈希的
maxsize——缓存多少组传入参数,当值为None时禁用LRU特性且缓存可无限增长
typed——如果为True,则不同类型的参数会分别缓存(例如f(3)和f(3.0))
被装饰的函数带有一个cache_info()函数,调用该函数返回一个具名元组,包含hits(命中次数:缓存使用次数), misses(未命中次数:缓存次数), maxsize(最大缓存数量), currsize(当前缓存大小)。在多线程环境中hits和misses是不完全准确
被装饰的函数也有一个cache_clear()函数,用于清理/使缓存失效
原始的未经装饰的函数可以通过__wrapped__属性访问
补充:最近最久未使用算法(LRU)
# 摘抄自:https://zhuanlan.zhihu.com/p/60731274
页面置换算法的一种。LRU算法根据数据的历史访问记录来进行淘汰数据。核心思想是“如果数据最近被访问过,那么将来被访问的几率也更高”
示例:
运行结果: # 其中一次运行结果
1.函数调用时间:0.006s
2.函数调用时间:0.005s
3.函数调用时间:0.004s
4.函数调用时间:0.006s
5.函数调用时间:0.0s
6.函数调用时间:0.0s
7.函数调用时间:0.006s
CacheInfo(hits=2, misses=5, maxsize=1, currsize=1)
3. @functools.total_ordering
装饰一个包含__lt__(), __le__(), __gt__()或__ge__()比较排序方法,且支持__eq__()方法的类,用来实现剩余的方法
# 虽然此装饰器使得创建具有良好行为的完全有序类型变得非常容易,但它确实是以执行速度更缓慢和派生比较方法的堆栈回溯更复杂为代价的。如果性能基准测试表明这是特定应用的瓶颈所在,则改为实现全部六个富比较方法应该会轻松提升速度
示例:
运行结果:
False False False True
4. partial(func /, *args, **keywords)
返回一个partial对象,当这个对象被调用行为类似于func,但会附带位置参数args和关键字参数keywords调用(像是“冻结了”func的一部分位置参数/关键字参数)
partial对象有三个只读属性,都是调用时的传入参数:
func, args, keywors
# partial对象不会自动创建__name__和__doc__属性,且在类中定义partial行为类似静态方法,不会在实例属性查找期间转换为绑定方法
示例:
运行结果:
3 1 4
20
5. partialmethod(func, /, *args, **keywords)
返回一个新的partialmethod描述器,行为类似partial()但被设计用作方法定义
6. reduce(function, iterable[, initializer])
将需传入两个参数的function从左至右累积地应用到iterable的条目,以便将该可迭代对象缩减为单一的值
initializer——它会被放在参与计算的可迭代对象的条目之前,并在可迭代对象为空时作为默认值
# 类似于itertools.accumulate(),但它只返回一个最终累积值
# 实测initializer不能用关键字传参
示例:
reduce(lambda x, y: x + y, [1, 2, 3, 4], 100) --> 110
itertools.accumulate([1, 2, 3, 4], lambda x, y: x + y, initial=100) --> [100, 101, 103, 106, 110]
补充:泛型函数(generic function)
为不同的类型实现相同操作的多个函数所组成的函数。在调用时会由调度算法来确定应该使用哪个实现
补充:单分派和多分派(single dispatch & multi dispatch)
# 参考:https://www.cnblogs.com/wongbingming/p/10726698.html
单分派是根据一个参数的类型,以不同方式执行相同的操作的行为
多分派是根据多个参数的类型选择专门的函数的行为
补充:重载(Overload)
# 参考:https://zhuanlan.zhihu.com/p/313572294
重载是在一个类里面,方法名相同,而参数不同。返回类型可以相同也可以不同
# 重写(Override)是在子类和父类之间的,方法名,参数相同。返回类型相同
7. @functools.singledispatch
将一个函数转换为单分派泛型函数
分派作用于第一个参数的类型。调用时,泛型函数根据第一个参数的类型分派
使用泛型函数的register装饰器将重载的实现添加到函数中。对于有类型标注的函数,装饰器会自动推动第一个参数的类型;对应没有类型标注的函数,可将适当类型参数显示传递给装饰器本身
使用函数形式的register(类型, 函数)可注册现有函数和匿名函数
register()属性装饰的函数未真正被装饰
使用泛型函数的dispatch(类型)属性检查对应类型的实现
使用只读属性registry访问所有已注册的实现
示例:
运行结果: # 其中一次运行结果
False False False
<function <lambda> at 0x000000000232C550>
{<class 'object'>: <function func at 0x0000000001E0FEE0>, <class 'int'>: <function func_int at 0x000000000232C4C0>, <class 'float'>: <function func_float at 0x00000000023AF160>, <class 'bool'>: <function <lambda> at 0x000000000232C550>}
1
2
3
4
8. singledispatchmethod(func)
将一个方法转换为单分派泛型方法
要定义一个泛型方法,应使用@singledispatchmethod装饰器进行装饰
分派是作用于第一个非self或非cls参数的类型
如果要用“单分派泛型方法.register”,则@singledispatchmethod装饰器必须是最外层的装饰器
9. update_wrapper(wrapper, wrapped, assigned=WRAPPER_ASSIGNMENTS,
updated=WRAPPER_UPDATES)
更新一个包装器函数wrapper以使其类似于被包装的函数wrapped
通过可选参数指明原函数的那些属性要直接被赋给wrapper函数的匹配属性的元组,并且这些wrapper函数的属性将使用原函数的对应属性来更新
这些参数的默认值是模块级常量WRAPPER_ASSIGNMENTS(文档字符串)以及WRAPPER_UPDATES(实例字典__dict__)
出于内省和其他目的访问原始函数,此函数会自动为wrapper添加一个指向被包装函数的__wrapped__属性
10. @functools.wraps(wrapped, assigned=WRAPPER_ASSIGNMENTS,
updated=WRAPPER_UPDATES)
便捷函数,用于在定义包装器函数时发起调用update_wrapper()作为函数装饰器
等价于partial(update_wrapper, wrapped=wrapped, assigned=assigned,
updated=updated)
示例:
运行结果: # 其中一次运行结果
--------func1:
wrapper
None
{}
--------func2:
func2
func2
{'__wrapped__': <function func2 at 0x0000000002318DC0>}