【Python基础】Python中的高效迭代库itertools,排列组合随便求
本文目录
一、模块概述
二、组合生成器
2.1 product
2.2 permutations
2.3 combinations
2.4 combinations_with_replacement
三、无限迭代器
3.1 count
3.2 cycle
3.3 repeat
四、有限迭代器
4.1 accumulate
4.2 chain
4.3 chain.from_iterable
4.4 compress
4.5 dropwhile
4.6 filterfalse
4.7 groupby
4.8 islice
4.9 starmap
4.10 takewhile
4.11 tee
4.12 zip_longest
五、总结
一、模块概述
Functional tools for creating and using iterators,为高效循环而创建迭代器的函数。本模块标准化了一个快速、高效利用内存的核心工具集,这些工具本身或组合都很有用。它们一起形成了“迭代器代数”,这使得在纯Python中有可能创建简洁又高效的专用工具。
permutations函数可以求序列的排列,combinations函数可以求序列的组合,除了这两个函数外,itertools还有相当多的功能,它主要是提供迭代类的操作。
迭代器的特点是:惰性求值(Lazy evaluation),即只有当迭代至某个值时,它才会被计算,这个特点使得迭代器特别适合于遍历大文件或无限集合等,因为我们不用一次性将它们存储在内存中。itertools 模块提供的迭代器函数有以下几种类型:
组合生成器:序列的排列、组合,求序列的笛卡儿积等,比如product可以生成两个序列的笛卡尔积;
无限迭代器:生成一个无限序列,比如count函数能生成自然数序列 1, 2, 3, 4, ...
有限迭代器:接收一个或多个序列(sequence)作为参数,进行组合、分组和过滤等,比如chain函数,能组合多个序列
print(dir(itertools))
['__doc__', '__loader__', '__name__', '__package__', '__spec__',
'_grouper', '_tee', '_tee_dataobject', 'accumulate', 'chain',
'combinations', 'combinations_with_replacement', 'compress',
'count', 'cycle', 'dropwhile', 'filterfalse', 'groupby', 'islice',
'permutations', 'product', 'repeat', 'starmap', 'takewhile', 'tee',
'zip_longest']
总之该模块为Python为创建自定义迭代器提供了非常好用的模块——itertools。由itertools提供的工具通常都很快速而且节省内存。你可以利用这些组成部分去创建自己独有的迭代器以完成高效的循环
官方参考文档:https://docs.python.org/zh-cn/3/library/itertools.html
二、排列组合迭代器
迭代器 |
实参 |
结果 |
product() |
p, q, ... [repeat=1] |
笛卡尔积,相当于嵌套的for循环 |
permutations() |
p[, r] |
长度r元组,所有可能的排列,无重复元素 |
combinations() |
p, r |
长度r元组,有序,无重复元素 |
combinations_with_replacement() |
p, r |
长度r元组,有序,元素可重复 |
例子 |
结果 |
product('ABCD', repeat=2) |
AA AB AC AD BA BB BC BD CA CB CC CD DA DB DC DD |
permutations('ABCD', 2) |
AB AC AD BA BC BD CA CB CD DA DB DC |
combinations('ABCD', 2) |
AB AC AD BC BD CD |
combinations_with_replacement('ABCD', 2) |
AA AB AC AD BB BC BD CC CD DD |
2.1 product
作用:用于求多个可迭代对象的笛卡尔积,它跟嵌套的 for 循环等价
语法:itertools.product(iter1, iter2, ... iterN, [repeat=1])
for each in itertools.product('ABCD', 'XY'):
print(each)
('A', 'X')
('A', 'Y')
('B', 'X')
('B', 'Y')
('C', 'X')
('C', 'Y')
('D', 'X')
('D', 'Y')
for each in itertools.product('abc', repeat=2):
print (each)
('a', 'a')
('a', 'b')
('a', 'c')
('b', 'a')
('b', 'b')
('b', 'c')
('c', 'a')
('c', 'b')
('c', 'c')
2.2 permutations
作用:返回长度为r的排列
语法:itertools.permutations(iteravle[,r])
iterable: 可迭代对象
r: 关键字参数, 新元素的长度, 默认为 None, 即为新元素的长度就是元素个数
将 iterable 中的元素以长度为 r 进行排列。每个排列组合生成一个元组并添加到新迭代器中。排列时,排列顺序是按照 iterable 中元素的顺序进行的。因此,未排序的 iterable 和已排序的 iterable 排列后的结果是不同的。
若 iterable 中有相同值的元素,但是它们的位置是不同的,排列时也就会会认为它们是不同的。注意: 排列即为数学上的排列,从 n 个元素中取出 m 个元素的无重复排列或直线排列。
for each in itertools.permutations('abc', 2):
print (each)
('a', 'b')
('a', 'c')
('b', 'a')
('b', 'c')
('c', 'a')
('c', 'b')
2.3 combinations
作用:返回指定长度的组合
语法: itertools.combinations(iterable, r)
iterable: 可迭代对象
r: 新元素的长度
for each in itertools.combinations('abc', 2):
print (each)
('a', 'b')
('a', 'c')
('b', 'c')
将 iterable 中的元素以长度为 r 进行组合。每个排列组合生成一个元组并添加到新迭代器中。与 permutations() 函数基本一致,但是 combinations() 函数会过滤掉元素值一致的元组。
'''相比于排列,组合会筛选掉元素值一样的元组'''
list(itertools.combinations('ABC', 2))
[('A', 'B'), ('A', 'C'), ('B', 'C')]
'''排列对照组'''
list(itertools.permutations('ABC', 2))
[('A', 'B'), ('A', 'C'), ('B', 'A'), ('B', 'C'), ('C', 'A'), ('C', 'B')]
2.4 combinations_with_replacement
作用:返回指定长度的组合,组合内元素可重复
语法:itertools.combinations_with_replacement(iterable, r)
iterable: 可迭代对象
r: 关键字参数, 新元素的长度, 默认为 None, 即为新元素的长度就是元素个数
for each in itertools.combinations_with_replacement('abc', 2):
print (each)
('a', 'a')
('a', 'b')
('a', 'c')
('b', 'b')
('b', 'c')
('c', 'c')
三、无限迭代器
有三个无限迭代器,当你使用它们的时候,你要知道何时去跳出这个循环,不然你将陷入无限循环之中,在生产数字或者遍历事先不知道长度的数据时非常有用。
迭代器 |
实参 |
结果 |
示例 |
count() |
start, [step] |
start,start+step,start+2*step, ... |
count(10) --> 10 11 12 13 14 ... |
cycle() |
p |
p0, p1, ... plast, p0, p1, ... |
cycle('ABCD') --> A B C D A B C D ... |
repeat() |
elem [,n] |
elem, elem, elem, ... 重复无限次或n次 |
repeat(10, 3) --> 10 10 10 |
3.1 count()
作用:返回以start开始,step递增的序列,无限递增
语法:itertools.count(start=0,step=1)
for each in itertools.count(start=0, step=2):
print (each)
1
2
...
100003
'''小应用, 当取到第三个 A 时停止'''
test_str = ''
for i in itertools.cycle('ABC'):
test_str += i
if test_str.count('A') >= 3:
break
print(test_str)
# ABCABCA
3.2 cycle()
作用:将迭代器进行无限迭代
语法:itertools.cycle(iterable)
for each in itertools.cycle('ab'):
print(each)
a
b
...
b
a
3.3 repeat()
作用:不停的返回object对象,如果指定了times,则返回times次
语法:itertools.repeat(object,[,times])
for each in itertools.repeat('ab', 2):
print(each)
ab
ab
四、有限迭代器
迭代器 |
实参 |
结果 |
示例 |
accumulate() |
p [,func] |
p0, p0+p1, p0+p1+p2, ... |
accumulate([1,2,3,4,5]) --> 1 3 6 10 15 |
chain() |
p, q, ... |
p0, p1, ... plast, q0, q1, ... |
chain('ABC', 'DEF') --> A B C D E F |
chain.from_iterable() |
iterable |
p0, p1, ... plast, q0, q1, ... |
chain.from_iterable(['ABC', 'DEF']) --> A B C D E F |
compress() |
data, selectors |
(d[0] if s[0]), (d[1] if s[1]), ... |
compress('ABCDEF', [1,0,1,0,1,1]) --> A C E F |
dropwhile() |
pred, seq |
seq[n], seq[n+1], ... 从pred首次真值测试失败开始 |
dropwhile(lambda x: x<5, [1,4,6,4,1]) --> 6 4 1 |
filterfalse() |
pred, seq |
seq中pred(x)为假值的元素,x是seq中的元素。 |
filterfalse(lambda x: x%2, range(10)) --> 0 2 4 6 8 |
groupby() |
iterable[, key] |
根据key(v)值分组的迭代器 |
|
islice() |
seq, [start,] stop [, step] |
seq[start:stop:step]中的元素 |
islice('ABCDEFG', 2, None) --> C D E F G |
starmap() |
func, seq |
func(*seq[0]), func(*seq[1]), ... |
starmap(pow, [(2,5), (3,2), (10,3)]) --> 32 9 1000 |
takewhile() |
pred, seq |
seq[0], seq[1], ..., 直到pred真值测试失败 |
takewhile(lambda x: x<5, [1,4,6,4,1]) --> 1 4 |
tee() |
it, n |
it1, it2, ... itn 将一个迭代器拆分为n个迭代器 |
|
zip_longest() |
p, q, ... |
(p[0], q[0]), (p[1], q[1]), ... |
zip_longest('ABCD', 'xy', fillvalue='-') --> Ax By C- D- |
4.1 accumulate()
作用:创建一个迭代器,返回累积汇总值或其他双目运算函数的累积结果值(通过可选的 func 参数指定),,若传入 func,则按照 func 中的方法执行。
语法:itertools.accumulate(iterable[, func, *, initial=None])
iterable: 可迭代对象
func: 带有两个参数的函数, 可选参数,func 参数有几种用法。它可以被设为 min() 最终得到一个最小值,或者设为 max() 最终得到一个最大值,或设为 operator.mul() 最终得到一个乘积。摊销表可通过累加利息和支付款项得到。给iterable设置初始值并只将参数 func 设为累加总数可以对一阶 递归关系 建模。
initial: 关键字参数, 默认为 None, 若此参数传参, 则此参数作为 iterable 的第一个元素
'''默认情况, 返回累计汇总值'''
list(itertools.accumulate([1, 2, 3, 4, 5]))
[1, 3, 6, 10, 15]
'''方法修改为计算累计的乘积'''
import operator #该模块输出一系列对应Python内部操作符的函数
data = [3, 4, 6, 2, 1, 9, 0, 7, 5, 8]
list(itertools.accumulate(data, operator.mul))
[3, 12, 72, 144, 144, 1296, 0, 0, 0, 0]
'''方法修改为计算累计的最大值'''
list(itertools.accumulate(data, max))
[3, 4, 6, 6, 6, 9, 9, 9, 9, 9]
# Amortize a 5% loan of 1000 with 4 annual payments of 90
cashflows = [1000, -90, -90, -90, -90]
list(itertools.accumulate(cashflows, lambda bal, pmt: bal*1.05 + pmt))
[1000, 960.0, 918.0, 873.9000000000001, 827.5950000000001]
#Chaotic recurrence relation
#https://en.wikipedia.org/wiki/Logistic_map
logistic_map = lambda x, _: r * x * (1 - x)
r = 3.8
x0 = 0.4
inputs = repeat(x0, 36) # only the initial value is used
[format(x, '.2f') for x in accumulate(inputs, logistic_map)]
['0.40', '0.91', '0.30', '0.81', '0.60', '0.92', '0.29', '0.79', '0.63',
'0.88', '0.39', '0.90', '0.33', '0.84', '0.52', '0.95', '0.18', '0.57',
'0.93', '0.25', '0.71', '0.79', '0.63', '0.88', '0.39', '0.91', '0.32',
'0.83', '0.54', '0.95', '0.20', '0.60', '0.91', '0.30', '0.80', '0.60']
'''在迭代器起始位置添加一个元素, 方法不变, 返回累计汇总值'''
list(itertools.accumulate([1, 2, 3, 4, 5], initial=100))
# [100, 101, 103, 106, 110, 115]
'''在迭代器起始位置添加一个元素, 方法修改为计算累计的乘积'''
list(itertools.accumulate([1, 2, 3, 4, 5], lambda x, y: x * y, initial=10))
#[10, 10, 20, 60, 240, 1200]
4.2 chain()
作用:chain 接收多个可迭代对象作为参数,将它们『连接』起来,作为一个新的迭代器返回。
语法:chain(iterable1, iterable2, iterable3, ...)
list(itertools.chain([1, 2, 3], ('A', 'B', 'C'), {'一', '二', '三'}))
[1, 2, 3, 'A', 'B', 'C', '二', '一', '三']
list(itertools.chain( 'ABC', 'DEF'))
['A', 'B', 'C', 'D', 'E', 'F']
4.3 chain.from_iterable
作用:与 itertools.chain() 函数类似,但是参数是一个可迭代对象,将这个可迭代对象中元素一一添加到新迭代器中,如果元素是一个可迭代对象,那么会将这个元素内的元素一一添加到新迭代器中。小编自己的理解就是迭代器降维。
语法:itertools.chain.from_iterable(iterable)
'''将二维迭代器降维'''
temp = itertools.chain.from_iterable(['1', ['2', '3'], ('4', '5')])
print(list(temp))
# ['1', '2', '3', '4', '5']
'''只能降一层维度, 三维将至二维'''
temp = itertools.chain.from_iterable(['1', ['2', '3'], ('4', ['5', '6'])])
print(list(temp))
# ['1', '2', '3', '4', ['5', '6']]
PS: 迭代器维度概念可以理解为,整个迭代器中的元素都不是迭代器类型的就是一维,迭代器中的元素有是迭代器的就是二维,迭代器中的元素有是迭代器的,然后这个迭代器中还有元素是迭代器的就是三维,依此类推。
4.4 compress
作用:创建一个迭代器,将 data 中经过 selectors 真值测试为 True 的元素保留。当两个可迭代对象中的某一个到达末尾时执行停止,返回最终结果。
语法:compress(data, selectors)
data: 可迭代对象, 包含所需的数据
selectors: 可迭代对象, 真值测试数据
for each in itertools.compress('abcd', [1, 0, 1, 0]):
print (each)
a
c
'''只判断前三个元素, 并且索引值为 0 和 2 的元素会保留到新迭代器中并返回'''
temp = itertools.compress(['A', 'B', 'C', 'D'], [1, 0, 1])
list(temp)
# ['A', 'C']
4.5 dropwhile
作用:直到predicate为真,就返回iterable剩下的数据, 否则drop掉
语法:itertools.dropwhile(predicate, iterable)
for each in itertools.dropwhile(lambda x: x<5, [2,1,6,8,2,1]):
print(each)
6
8
2
1
4.6 filterfalse
作用:创建一个迭代器,仅保留 iterable 中在 predicate 计算中为 False 的元素。如果 predicate 传入的是 None,则相当于传入 bool,意思是做真值测试。
语法:itertools.filterfalse(predicate, iterable)
predicate: 只需要一个参数的函数
iterable: 可迭代对象
'''元素的值减2小于等于0的为True'''
temp = itertools.filterfalse(lambda x: x - 2 <= 0, [1, 2, 0, 3, 1])
print(list(temp))
# [3]
'''真值测试'''
temp = itertools.filterfalse(None, [1, 2, 0, 3, 1])
print(list(temp))
# [0]
4.7 groupby
作用:返回一组(key,itera),key为iterable的值,itera为等于key的所有项
语法:itertools.groupby(iterable[,key])
for key, vale in itertools.groupby('aabbbc'):
print( key, list(vale))
a ['a', 'a']
b ['b', 'b', 'b']
c ['c']
4.8 islice
作用:相当于迭代器方式的切片操作
语法:itertools.islice(iterable, start,stop[,step])
for each in itertools.islice('abcdefg', 1, 4, 2):
print(each)
b
d
4.9 starmap()
作用:返回function(iter)的值,iter为iterable的元素
语法:itertools.starmap(function,iterable)
for each in itertools.starmap(lambda x, y: x * y, [(1, 2), (3, 4)]):
print(each)
2
12
4.10 takewhile()
作用:创建一个迭代器,将 iterable 中的元素当作 function 的参数计算,与 dropwhile() 函数恰恰相反,当结果的布尔值为 True 时,其元素添加到新迭代器中, 直到有元素的计算结果为 False 时,此元素与之后的元素全部抛弃。
语法:itertools.takewhile(predicate,iterable)
for each in itertools.takewhile(lambda x: x < 5, [1, 3, 5, 6]):
print(each)
1
3
4.11 tee
作用:从一个可迭代对象中分裂出 n 个独立的可迭代对象并返回。
语法:itertools.tee(iterable, n=2)
for i in itertools.tee([1, 2, 3, 4, 5, 6], 2):
print(list(i))
# [1, 2, 3, 4, 5, 6]
# [1, 2, 3, 4, 5, 6]
4.12 zip_longest
作用:创建一个迭代器,从所有传入的可迭代对象中抽取一个元素,将它们组合成一个元组,然后添加到迭代器中。
语法:itertools.zip_longest(*iterables, fillvalue=None)
iterables: 一个或多个可迭代对象
fillvalue: 关键字参数, 填充值, 默认为 None
list(itertools.zip_longest('xyz', '123456', '小伍哥', fillvalue='*'))
[('x', '1', '小'),
('y', '2', '伍'),
('z', '3', '哥'),
('*', '4', '*'),
('*', '5', '*'),
('*', '6', '*')]
五、总 结
迭代器(生成器)在Python中是一种很常用也很好用的数据结构,比起列表(list)来说,迭代器最大的优势就是延迟计算,按需使用,从而提高开发体验和运行效率,以至于在Python 3中map,filter等操作返回的不再是列表而是迭代器。而通过iter函数把列表对象转化为迭代器对象又有点多此一举,这时候我们今天的主角itertools就该上场了。
itertools中的函数大多是返回各种迭代器对象,其中很多函数的作用我们平时要写很多代码才能达到,而在运行效率上反而更低,大概就总结到这里,不过Python的各种语言特性和库还是要多用才能熟练,最终达到随手拈来的程度。