胶水PythonPython小推车python模块

Python一天一模块:itertools 迭代器工具和排列组合

2019-02-14  本文已影响9人  爱折腾的大懒猪

itertools 是实现各种迭代生成的工具, 尤其重要的是可以实现排列和组合的问题. 返回的一般都是迭代器.

合并之前三篇相关的内容

一. 组合式迭代器

1. product

能够从多个迭代器中各自取值组成组合,生成的元组数量为N*M*O.... 实际上可以用多个for-loop来实现. 但如果输入数量不确认时, 用for循环就不太好写了. 因此更建议用prodcut实现.

格式是: product(*iterables, repeat=1), 相当于多层次的for循环嵌套, 第一层是第一个迭代器, 以此类推. 当使用repeat=N时, 就是迭代N次前面的迭代器. 适合重复迭代某一个迭代器.

product('ab', range(3)) 
#--> ('a',0) ('a',1) ('a',2) ('b',0) ('b',1) ('b',2)
product((0,1), (0,1), (0,1)) 
#--> (0,0,0) (0,0,1) (0,1,0) (0,1,1) (1,0,0)
product('ab', repeat=2)
#--> ('a', 'a'), ('a', 'b'), ('b', 'a'), ('b', 'b')

a = [range(2), range(3)] 
product(*a)
#--> (0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2)

2. combinations

组合问题的解法, 返回的是N个元素选取r个的组合, 而且是有序的 (从小到大).

格式是: combinations(iterable, r), 返回生成 r 元元组的迭代器. 元组数量是r(上标)N(下标)C的数量. 返回的元组内的元素顺序依从原迭代器器内的元素的顺序. 当输入迭代器是有序且无重复时, 返回的是无重复的有序的元组的迭代器.

由于是一个组合问题, 所以当输入的列表含有相同元素时, 也会生成含有重复数值的元组. 但这个只是数值重复, 来源是来于不同位置.

from itertools import combinations
combinations(range(4), 3) 
#--> (0,1,2), (0,1,3), (0,2,3), (1,2,3)

combinations([1,1,2], 2)
#--> (1, 1), (1, 2), (1, 2)

3. permutations

排列问题的解法, 返回的是N个元素选取r个的组合, 无序组合, 因此实际是排列问题.

格式是: permutations(iterable[, r]), 返回生成 r 元元组的迭代器(不指名r时, r为迭代器的长度). 元组数量是r(上标)N(下标)A的数量.

实际上是取不同元素进行排列, 因此如果输入的迭代器含有相同值得元素时, 输出的元组也可能出现重复值, 但实际来源于不同元素.

from itertools import permutations
permutations(range(3), 2) 
#--> (0, 1), (0, 2), (1, 0), (1, 2), (2, 0), (2, 1)

permutations([1,1,2], 2)
#--> (2, 1), (2, 2), (1, 2), (1, 2), (2, 2), (2, 1)

4. combinations_with_replacement

放回式组合问题. 在普通组合中, 一个元素只能用一次. 该方法运行多次.

格式是: combinations_with_replacement(iterable, r), 和combinations一样. 和排列不一样的是, 元素的排列始终是有序的, 例如'ABCD', combinations_with_replacement会出现AA,AB, 不会出现BA. 差别可参考下列三个循环:

combinations_with_replacement('ABCD', 2)
#--> AA AB AC AD BB BC BD CC CD DD

求排列组合和阶乘: from scipy import special

  • special.perm(5,2) -> 排列A5,2的值. 20
  • special.com(5.2) -> 组合C5,2的值, 10.
  • special.factorial(5) -> 阶乘5, 120.

itertools还有两类迭代器, 一种是无穷迭代处理, 一种是对有限输入进行迭代处理. 官方叫: Infinite iteratorsIterators terminating on the shortest input sequence.

二. 无穷迭代器

实现无穷迭代, 一般配合for和判断条件来打断.

1. count

不断递增数值, 格式count(start=0 [,step=1]), 支持参数是递增值. 例如: count(10) --> 10 11 12 13 14 ...

2. cycle

对指定迭代对象不断迭代. 格式: cycle(iterable), 例如: cycle('ABCD') --> A B C D A B C D ...

3. repeat

重复某个值无穷次或指定次数, 格式是repeat(object [,times]), times是重复次数, 默认是无穷循环. 例如repeat(10,3) --> 10 10 10.

三. 有限处理迭代器

accumulate 累加

accumulate(p [, function]) : 对之前的结果进行累加, 也可以自定义处理函数. 实际是result=a0, result+=a1, result+=a2, result+=a3....

例如: accumulate([1,2,3,4,5]) --> 1 3 6 10 15

chain 迭代器整合

将多个迭代器组合一起进行叠加, 例如chain(iter1, iter2, iter3), 迭代完第一个迭代器后接着迭代第二个. 其实就是把多个迭代器组合一起形成一个新迭代器. 挺重要的.

如果输入是迭代器包含迭代器, 例如iterlist=['ABC', 'DEF'], 可以使用chain(*iterlist), 也可以使用chain.from_iterable(iterlist).

takewhile dropwhile 按条件取舍迭代器.

这两个方法类似, takewhile是条件满足时留下, dropwhile是条件满足时抛弃.

格式: dropwhile(predicate, iterable), pred是一个 pred(item)函数对象, 返回True/False. 最简单的是采用lambda来组合完成.

takewhile(lambda x: x<5, [1,4,6,4,1]) 
#--> 1 4

dropwhile(lambda x: x<5, [1,4,6,4,1]) 
#--> 6 4 1

filterfalse 提取假的结果

格式是filterfalse(function or None, sequence), 如不指明判断的函数, 则会使用bool来判断. 返回的是迭代器元素进行 function(item)后返回假的元素.

filterfalse(lambda x: x%2, range(10)) 
#--> 0 2 4 6 8

startmap 接受一个参数迭代器的map

startmap和map类似, 但接受的可以是函数参数已放好在迭代器中的迭代器. 例如pow函数接受两个参数, 使用map需要两个迭代器分别传递参数, 而startmap则可以采用一个迭代器, 迭代器产生的是所需参数的元组.

格式: starmap(function, sequence). map和startmap参数之间的转化也可以用zip来进行.

map(pow, range(10), repeat(2))
#-> 0, 1, 4, 9, 16, 25 .... 81

map(pow, [2,3,10], [5,2,3])
# --> 32 9 1000

starmap(pow, [(2,5), (3,2), (10,3)]) 
# --> 32 9 1000

para = zip( [(2,5), (3,2), (10,3)] )
# [(2,3,10), (5,2,3)]
map(*para)
# --> 32 9 1000

tee 迭代器复制

从一个原始迭代器返回n个独立不受干扰的迭代器. 实际测试中, 生成的n个迭代器中, 第一个和原始迭代器是同步的, 而后面的是完全独立, 和原始迭代器无关的. 生成的迭代器的状态和tee时的状态是一致的.

格式: tee(iterable, n=2) 注意不能明参n=3, 只能tee(iterable, 3). 返回有n个迭代器的元组.

a = itertools.chain(range(3),range(3,10))
a.__next__(); a.__next__()
# -> 1   # 下一个生成数是2

# b = itertools.tee(a,3) will get error
b = itertools.tee(a,3)
b[0].__next__()  #-> 2
a.__next__()      #-> 3
b[1].__next__()  #-> 2
b[0].__next__()  #-> 4
b[2].__next__()  #-> 2

islice 分片器(元素选择器)

能够实现类似分片的效果, 从迭代器中取出指定范围的元素. 如果stop是None, 相当于[start:]效果, 迭代到底.

格式是: itertools.islice(iterable, stop)itertools.islice(iterable, start, stop[, step]).

islice('ABCDEFG', 2)               --> A B  == [:2]
islice('ABCDEFG', 2, 4)           --> C D  == [2:4]
islice('ABCDEFG', 2, None)     --> C D E F G  == [2:]
islice('ABCDEFG', 0, None, 2) --> A C E G  == [0::2], [::2]

compress 根据选择器选取结果

格式是compress(data, selectors), 其中data和selectors都是迭代器, 如果selectors出来的值是True或等价的值, 则返回相应data的元素的值. 如果两个迭代器长度不等, 根据短的进行处理(长的多出来的部分忽略, 不会报错).

compress('ABCDEF', [1,0,1,0,1,1]) 
# --> A C E F

groupby 连续重复的归为一组

格式: groupby(iterable, key=None) 对迭代器进行迭代, 根据key函数处理的值(如无, 则按元素的值进行判断)来判断元素是否和上一个元素相对, 是的话归为一组, 不是的话则创建一个新组, 新组的组名(组值)就是该元素. 类似unix命令的uniq.

# 根据字符串重复值分组, 查看相应组key和值
[k for k, g in groupby('AAAABBBCCDAABBB')] 
# --> A B C D A B
[list(g) for k, g in groupby('AAAABBBCCD')] 
# --> AAAA BBB CC D

# 假设有一个数据集, 想根据keyfunc来对内容进行排序并分组. 可以:
groups = []
uniquekeys = []
data = sorted(data, key=keyfunc)
for k, g in groupby(data, keyfunc):
    groups.append(list(g))      # Store group iterator as a list
    uniquekeys.append(k)

zip_longest

和zip类似, 将两个迭代器的值放在一起返回二元元组. 但是zip长度不等会报错, 而zip_longest则支持长度不等. 当长度不等时, 短的迭代器部分使用None或者指定的内容

格式: zip_longest(*iterables, fillvalue=None) , 返回 [(A,a), (B,b)....(F, None)]

zip_longest('ABCD', 'xy', fillvalue='-') 
# --> (A,x) (B,y) (C,-) (D,-)

zip_longest('ABCD', 'abc')
#--> (A, a), (B,b), (C,c) 

Reference

  1. 官方: itertools — Functions creating iterators for efficient looping
上一篇下一篇

猜你喜欢

热点阅读