itertools——创建迭代器的函数模块(提升了重复创建的效率)
该模块内嵌了一系列可用于迭代器构造的功能模块,它的灵感来自于APL、Hsakell与SML。所有的灵感都被重构为适合于Python的版本。
该模块标准化实现了一个可快速运行、高效存储数据的工具核心,它不止可以被模块自身使用,也可以与其余模块进行协作。并且,它通过“迭代器代数”使得模块间的协作可以构造出一个专业、简洁而高效工具,并可以用纯Python实现。
例如,SML提供了一个表格工具:tabulate(f)——该工具生成一个序列f(0),f(1),… 。如果要实现相同的效果,在Python中就可以让map()与count()协作,使用map(f, count())即可达成目的。
这些工具和它们的内置部分也可以与operator模块内的高速函数良好协作。例如,乘法运算可以通过两个向量的映射成为一个搞笑的点积运算:
sum(map(operator.mul, vector1, vector2))
无穷迭代器:(中括号内是选用参数)
迭代器 |
参数 |
结果 |
例子 |
count() |
start,[step] |
start,start+step,start+2*step,… |
count(10) —>10,11,12, |
cycle() |
p |
p0,p1,…P内元素被遍历完,po,p1 |
cycle(‘ABCD’) —>A B C D A B … |
repeat() |
elem[,n] |
elem,elem,elem,…无穷无尽或者重复了n次后就停止 |
repeat(10, 3) è 10 10 10 |
迭代器在最短输入序列遍历完毕后停止:
迭代器 |
参数 |
结果 |
例子 |
accumulate() |
p[, func] |
po,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第一次无效后后开始 |
drowpwhile(lambda x: x<5, [1,4,5,41,]) —>6 4 1 |
filterfalse() |
pred, seq |
当pred中的某个元素对应的布尔值为False,输出与其相同索引数字的seq中的元素 |
filterfalse(lambda x: x%2, range(10)) —>0 2 4 6 8 |
groupby() |
iterable[, key] |
按key值对iterable进行分组 |
|
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 100 |
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,… 将一个迭代器分割为n个 |
|
zip_longest() |
p, q,… |
(p[0],q[0]),(p[1],q[1]),… |
zip_longest(‘abcd’, ‘xy’, fillvalue=’-’) è ax by c- d- |
组合迭代器:
迭代器 |
参数 |
结果 |
product() |
p, q,…[repeat=1] |
笛卡尔积(直积,相同索引下的元素相乘),等价于嵌入了for循环 |
permutations() |
p[,r] |
长度为r的元组,用p中的元素排列组合出所有可能的情况,其中不应当有重复的元素 |
combinations(0 |
p, r |
长度为r的元组,元素是排序好了的,可以有重复元素 |
combinations_with_replacement() |
p, r |
长度为r的元组,元素是排序好了的,不存在重复元素 |
product(‘ADBCD’, 2) |
|
AB AC AD BA BC BD CA CB CC CD DA DB DC DD |
combinations(‘ABCD’, 2) |
|
AB AC AD BC BD CD |
permutations(‘ABCD’, 2) |
|
AB AC AD BA BC BD CA CB CD DA DB DC |
combinations_with_replacement(‘ABCD’, 2) |
|
AA AB AC AD BB BC BD CC CD DD |
1.itertool函数
下列的的模块函数都是用来构建或返回迭代器。有些函数提供不限长度的流输出,因此它们应该只能够通过可以截断流输出的函数或循环来访问。
itertools. accumulate(iterable[, func])
生成一个迭代器,该迭代器返回累计和,或者其它二进制函数(通过func参数指定)的累计结果。如果给出func,func应代表一个具有两个参数的函数。输入的iterable参数对应的可迭代数据内的每一个元素都应当属于func指定函数可以接受的数据类型。(例如,对于默认的求和操作,元素应当为可求和数据,比如Decimal小数和Fraction分数。)如果输入的iterable是空的,那么输出的可迭代数据也是空的。
该函数大致等价于:
defaccumulate(iterable, func=operator.add):
'Returnrunning totals'
# accumulate([1,2,3,4,5]) --> 1 3 6 10 15
# accumulate([1,2,3,4,5], operator.mul) --> 1 2 6 24 120
it =iter(iterable)
try:
total =next(it)
exceptStopIteration:
return
yield total
for element in it:
total = func(total, element)
yield total
func参数的用法有很多种。它可以设置为min()以运行最小值,设置为max()以运行最大值,设置为operator.mul()以运行求积。摊销表可以通过累积利息和支付款项来建立。一阶递推关系可以通过在迭代中提供初始值并且仅使用FUNC参数中的累计总数来建模:
>>>data = [3, 4, 6, 2, 1, 9, 0, 7, 5, 8]
>>>list(accumulate(data, operator.mul)) # running product
[3, 12,72, 144, 144, 1296, 0, 0, 0, 0]
>>>list(accumulate(data, max)) # running maximum
[3, 4, 6,6, 6, 9, 9, 9, 9, 9]
# Amortizea 5% loan of 1000 with 4 annual payments of 90
>>>cashflows = [1000, -90, -90, -90, -90]
>>>list(accumulate(cashflows, lambda bal, pmt: bal*1.05+ pmt))
[1000,960.0, 918.0, 873.9000000000001, 827.5950000000001]
# Chaoticrecurrence 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']
functools.reduce()与其相似,只返回最后的累计结果。
itertools. chain(*iterable)
生成一个迭代器返回来自于第一个可迭代对象的元素,直到其元素耗尽,之后对下一个可迭代对象重复相同的过程,直到把参数中所有的可迭代对象遍历完毕。它可以把多个连续序列当作一个序列处理。其代码大致等价于:
defchain(*iterables):
# chain('ABC', 'DEF') --> A B C D E F
for it in iterables:
for element in it:
yield element
classmethodchain.from_iterable(iterable)
chain()的替代构造器。从一个单个可迭代参数中得到链式输入,该参数应当是懒惰的(无法改变)。大致等价于:
deffrom_iterable(iterables):
# chain.from_iterable(['ABC', 'DEF']) --> A B C D E F
for it in iterables:
for element in it:
yield element
itertools. combinations(iterable, r)
返回长度为r的序列,其元素来自于输入参数iterable。
组合按照词典排序的方式的顺序输出数据。因此,如果输入的iterable是有序的,组合元组将按排序顺序生成。
所有元素被认为是独一无二的,这种认知是基于元素所在的位置而非其值的大小。因此,如果输入元素没有重复,组合出来的结果也不会存在重复值。
该函数大致等价于:
defcombinations(iterable, r):
# combinations('ABCD', 2) --> AB AC AD BC BD CD
# combinations(range(4), 3) --> 012 013 023 123
pool =tuple(iterable)
n =len(pool)
if r > n:
return
indices =list(range(r))
yieldtuple(pool[i] for i in indices)
whileTrue:
for i inreversed(range(r)):
if indices[i] != i + n - r:
break
else:
return
indices[i] +=1
for j inrange(i+1, r):
indices[j] = indices[j-1] +1
yieldtuple(pool[i] for i in indices)
combinations()的代码也被表示为permutations()的子序列经输入未排序元素后进行过滤的形式(排序是根据它们在输入池内的位置而进行)。
defcombinations(iterable, r):
pool =tuple(iterable)
n =len(pool)
for indices in permutations(range(n), r):
ifsorted(indices) ==list(indices):
yieldtuple(pool[i] for i in indices)
当0<=r<=n或r>n时,返回的醒目总数为n!/r!/(n-r)!。
itertools. combinations_with_replacement(iterable, r)
返回长度为r的子序列,其元素来自于输入参数iterable,该函数允许iterable中的个别元素重复出现。
组合安装词典排序的方法顺序输出数据。因此,如果输入的iterable已经有序,组合元组将会按顺序生成。
元素的唯一性体现在其位置信息上而非其值。因此如果输入元素无重复,生成的组合也将无重复。
大致等价于:
defcombinations_with_replacement(iterable, r):
# combinations_with_replacement('ABC', 2) --> AA AB AC BB BC CC
pool =tuple(iterable)
n =len(pool)
ifnot n and r:
return
indices = [0] * r
yieldtuple(pool[i] for i in indices)
whileTrue:
for i inreversed(range(r)):
if indices[i] != n -1:
break
else:
return
indices[i:] = [indices[i] +1] * (r - i)
yieldtuple(pool[i] for i in indices)
combinations_with_replacement()的代码也可以被表示为product()的子序列在对未排序输入元素进行过滤后形式(排序的原则是根据其在输入池内的位置)。
defcombinations_with_replacement(iterable, r):
pool =tuple(iterable)
n =len(pool)
for indices in product(range(n), repeat=r):
ifsorted(indices) ==list(indices):
yieldtuple(pool[i] for i in indices)
当n>0时,输出的项目总数为(n+r-1)!/r!/()n-1!。
itertools. compress(data, selectors)
产生一个迭代器,它在数据中筛选元素,当它遇到与selectors中评估为真的元素相同的元素时,将这个元素返回。当data或selectors迭代器被耗尽时,该函数停止返回数据。
大致等价于:
defcompress(data, selectors):
# compress('ABCDEF', [1,0,1,0,1,1]) --> A C E F
return (d for d, s inzip(data, selectors)if s)
itertools. count(start=0, step=1)
产生一个迭代器,返回从索引start开始的杰哥为step的值。它经常作为map()的一个参数,用来生成连续的数据点。此外,它可以与zip()仪器使用从而为其添加序号。其大致等价于:
defcount(start=0, step=1):
# count(10) --> 10 11 12 13 14 ...
# count(2.5, 0.5) -> 2.5 3.0 3.5 ...
n = start
whileTrue:
yield n
n += step
当使用浮点数计数时,有事可以替代乘法代码来实现更高的精度。
例如:(start +step* i for i in count())
itertools. cycle(iterable)
产生一个迭代器返回来自于iterable的元素,并且保存所有元素的副本。当迭代器被耗尽,将从保存的副本中返回元素。该过程可以重复无数次。其大致等价于:
defcycle(iterable):
# cycle('ABCD') --> A B C D A B C D A B C D ...
saved = []
for element in iterable:
yield element
saved.append(element)
while saved:
for element in saved:
yield element
注意,工具箱的这个成员可能会消耗你大量的内存(消耗的多少取决于迭代器的长度)。
itertools. dropwhile(predicate, iterable)
生成一个迭代器,只要predicate为真,就从iterable中删除元素;当predicate变为假时,该迭代器返回剩余的每个元素。注意,迭代器在predicate变为假前不产生任何输出,因此该函数可能具有较长的启动时间。其等价为:
defdropwhile(predicate, iterable):
# dropwhile(lambda x: x<5, [1,4,6,4,1]) --> 6 4 1
iterable =iter(iterable)
for x in iterable:
ifnot predicate(x):
yield x
break
for x in iterable:
yield x
itertools. filterfalse(predicate, iterable)
产生一个迭代器,它的返回元素是iterable中使得predicate为假的元素。如果predicate为None,返回那些本身布尔属性为假的项目。大致等价于:
deffilterfalse(predicate, iterable):
# filterfalse(lambda x: x%2, range(10)) --> 0 2 4 6 8
if predicate isNone:
predicate =bool
for x in iterable:
ifnot predicate(x):
yield x
itertools. groupby(iterable, key=None)
产生一个迭代器,它从iterable返回连续的键和组。key是一个为每个元素计算键值的函数。如果key没有给出或者为None,key默认为标识函数,其返回值与输入值保持一致。一般来说,iterable需要已经使用相同的key函数进行排序。
groupby()的操作类似于Unix中的uniq过滤器。每当key函数的值发生改变时,它都会产生一个终端或者新组(这也就是为什么通常必须使用相同的key函数进行排序)。这种行为与SQL语言中的GROUP BY语句不同,该函数聚集公共元素而无视其输入顺序。
该函数返回的组本身就是一个迭代器,它与groupby()共享基础迭代。由于资源是共享的,当groupby()对象被推动时,前一组的数据将不再可见。因此,如果数据稍后需要使用,它应该被存储在列表当中:
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)
groupby()大致等价于:
classgroupby:
# [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
def__init__(self, iterable, key=None):
if key isNone:
key =lambda x: x
self.keyfunc = key
self.it =iter(iterable)
self.tgtkey =self.currkey =self.currvalue =object()
def__iter__(self):
returnself
def__next__(self):
whileself.currkey ==self.tgtkey:
self.currvalue =next(self.it) # Exit on StopIteration
self.currkey =self.keyfunc(self.currvalue)
self.tgtkey =self.currkey
return (self.currkey, self._grouper(self.tgtkey))
def_grouper(self, tgtkey):
whileself.currkey == tgtkey:
yieldself.currvalue
try:
self.currvalue =next(self.it)
exceptStopIteration:
return
self.currkey =self.keyfunc(self.currvalue)
itertools. islice(iterable, stop)
itertools. islice(iterable, start, stop[, step])
生成一个迭代器,该迭代器返回从iterable中选择的元素。如果start非零,那么Iterable中先于start的元素将被跳过。之后,元素将陆续的被返回,直到step(步长)的设置超出本次容许跳过的最大值。如果stop是None,那么迭代会一直继续,直到迭代器被耗尽;否则,它将会停在特定的位置。不同于常规切片,islice()不允许start、stop和step被设置为负数。该函数可以用于提取扁平化数据内部的相关字段(例如:多行报表可以在其第三行列出名称字段)。源代码大致等价于:
defislice(iterable,*args):
# islice('ABCDEFG', 2) --> A B
# islice('ABCDEFG', 2, 4) --> C D
# islice('ABCDEFG', 2, None) --> C D E F G
# islice('ABCDEFG', 0, None, 2) --> A C E G
s =slice(*args)
start, stop, step = s.startor0, s.stopor sys.maxsize, s.stepor1
it =iter(range(start, stop, step))
try:
nexti =next(it)
exceptStopIteration:
# Consume *iterable* up to the *start* position.
for i, element inzip(range(start), iterable):
pass
return
try:
for i, element inenumerate(iterable):
if i == nexti:
yield element
nexti =next(it)
exceptStopIteration:
# Consume to *stop*.
for i, element inzip(range(i+1, stop), iterable):
pass
如果start为None,迭代将从零开始。如果step为None,步长默认为1。
itertools. permutations(iterable, r=None)
从Iterable中返回长度为r的元素组合序列。
如果r没有给出或者为None,那么r使用iterable的长度为其默认值,并且将会依照排序生成所有的组合元祖。
组合按照词典的排序方式排序。因此,如果输入的iterable有序,那么组合元祖将按照排序指令顺序生成。
元素按它所处的位置确定其独一性,而非使用其值。因此,如果输入元素独一无二䦹,组合中将不会出现重复的值。
源代码大致等价于:
defpermutations(iterable, r=None):
# permutations('ABCD', 2) --> AB AC AD BA BC BD CA CB CD DA DB DC
# permutations(range(3)) --> 012 021 102 120 201 210
pool =tuple(iterable)
n =len(pool)
r = n if r isNoneelse r
if r > n:
return
indices =list(range(n))
cycles =list(range(n, n-r,-1))
yieldtuple(pool[i]for i in indices[:r])
while n:
for i inreversed(range(r)):
cycles[i]-=1
if cycles[i]==0:
indices[i:]= indices[i+1:]+ indices[i:i+1]
cycles[i]= n - i
else:
j = cycles[i]
indices[i], indices[-j]= indices[-j], indices[i]
yieldtuple(pool[i]for i in indices[:r])
break
else:
return
permutations()的代码也可以被表达为product()的子序列,该子序列已过滤其中重复的元素(以元素在输入池中的位置确定其是否重复)。
defpermutations(iterable, r=None):
pool =tuple(iterable)
n =len(pool)
r = n if r isNoneelse r
for indices in product(range(n), repeat=r):
iflen(set(indices))== r:
yieldtuple(pool[i]for i in indices)
返回的项目书为n!/(n-1)!,当0<=r<=n;或者当r>n时,数目为0.
itertools. product(*iterables, repeat=1)
产生输入的iterables的笛卡尔积。
大致等价于生成器表达式中嵌套的for循环。例如,product(A,B)的返回值与((x, y) forx in A for y in B)相同。
嵌套循环类似于里程计,在每次迭代中,最右端的元素向前移动。这种模式创造出一种词典排序方式,因此如果输入的iterables有序,那么生成元组按照排序命令有序生成。
为了用其本身计算出一个可迭代的乘积,需使用可选参数repeat明确重复的次数。例如,product(A,repeat=4)与product(A,A, A, A)的意思相同。
这个函数大致等价于下列代码,但要注意,在实际实现中并没有在内存中建立中间结果存储机制。
defproduct(*args, repeat=1):
# product('ABCD', 'xy') --> Ax Ay Bx By Cx Cy Dx Dy
# product(range(2), repeat=3) --> 000 001 010 011 100 101 110 111
pools =[tuple(pool)for pool in args]* repeat
result =[[]]
for pool in pools:
result =[x+[y]for x in result for y in pool]
for prod in result:
yieldtuple(prod)
itertools. repeat(object[, times])
生成一个迭代器,多次返回object。它可以运行无数次,除非times参数给定了限制次数。它可以被当做map()的一个参数去实现不变参数调用函数。也可以作为zip()的一部分创造元组记录的不变部分。
大致等价于:
defrepeat(object, times=None):
# repeat(10, 3) --> 10 10 10
if times isNone:
whileTrue:
yieldobject
else:
for i inrange(times):
yieldobject
该函数的常用方法是为映射和压缩提供一个常量值输出流。
>>> list(map(pow,range(10), repeat(2)))
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
itertools. starmap(function, iterable)
使用获取于iterable的参数计算function,其结果放在迭代器中返回。当来自于单个iterable的参数被分组到元组中时,可以使用map()函数代替该函数(数组是被预先压缩的)。map()和starmap()之间的区别类似于function(a, b)与function(*c)的区别。其代码大致等价于:
defstarmap(function, iterable):
# starmap(pow, [(2,5), (3,2), (10,3)]) --> 32 9 1000
for args in iterable:
yield function(*args)
itertools. takewhile(predicate, iterable)
产生一个迭代器返回来自于iterable的元素,只用相对应的predicate为真时,iterable的元素才会被返回。
deftakewhile(predicate, iterable):
# takewhile(lambda x: x<5, [1,4,6,4,1]) --> 1 4
for x in iterable:
if predicate(x):
yield x
else:
break
itertools. tee(iterable, n=2)
从单个iterable中返回n个独立的迭代器。
下面的Python代码可以帮助解释tee到底实现了什么(尽管实际的实现更为复杂,并且仅仅使用了单个非浸没是FIFO队列)。
代码大致等价于:
deftee(iterable, n=2):
it =iter(iterable)
deques =[collections.deque()for i inrange(n)]
defgen(mydeque):
whileTrue:
ifnot mydeque: # when the local deque is empty
try:
newval =next(it) # fetch a new value and
exceptStopIteration:
return
for d in deques: # load it to all the deques
d.append(newval)
yield mydeque.popleft()
returntuple(gen(d)for d in deques)
一旦tee()生效,原始的iterable就不应当在其它地方被使用;否则,iterable就有可能产生移步,但这个地步tee对象是无法知晓的。
这个迭代工具可能需要较大的辅助存储器(取决于有多少临时数据要被存储)。一般来说,一个迭代器经常被用到或者所有的数据出现于另一个被使用的迭代器前,使用list()要比使用tee()更快。
itertools. zip_longest(*iterables, fillvalue=None)
产生一个迭代器,把来自多个迭代器内的元素都聚集起来。如果iterables长度不等,那么使用fillvalue填充缺失的值。迭代持续到最长的iterable被耗尽。大致等价于:
classZipExhausted(Exception):
pass
defzip_longest(*args,**kwds):
# zip_longest('ABCD', 'xy', fillvalue='-') --> Ax By C- D-
fillvalue = kwds.get('fillvalue')
counter =len(args)-1
defsentinel():
nonlocal counter
ifnot counter:
raise ZipExhausted
counter -=1
yield fillvalue
fillers = repeat(fillvalue)
iterators =[chain(it, sentinel(), fillers)for it in args]
try:
while iterators:
yieldtuple(map(next, iterators))
except ZipExhausted:
pass
如果有一个iterables是无穷的,那么zip_longest()函数就必须采取一些方法限制其调用次数(例如,islice()或takewhile())。如果没有明确,填充值默认为None。
2. 迭代器工具用法
该借展示了使用已有的迭代器工具作为模块创建额外的建立工具的方法。
扩展的工具提供了与隐含建立工具相同的表达力。为了保持较高的内存性能,这就需要每次处理一个元素而非每次将整个可迭代数据都放入内存中。通过链接存在于同一函数格式下的工具减少临时变量以保持代码的小体积。通过使用“矢量化”结构快,使用循环和生成器来获得解释器开销,从而保持高速。
deftake(n, iterable):
"Returnfirst n items of the iterable as a list"
returnlist(islice(iterable, n))
defprepend(value, iterator):
"Prependa single value in front of an iterator"
# prepend(1, [2, 3, 4]) -> 1 2 3 4
return chain([value], iterator)
deftabulate(function, start=0):
"Returnfunction(0), function(1), ..."
returnmap(function, count(start))
deftail(n, iterable):
"Returnan iterator over the last n items"
# tail(3, 'ABCDEFG') --> E F G
returniter(collections.deque(iterable, maxlen=n))
defconsume(iterator, n=None):
"Advancethe iterator n-steps ahead. If n is None, consume entirely."
# Use functions that consume iterators at C speed.
if n isNone:
# feed the entire iterator into a zero-length deque
collections.deque(iterator, maxlen=0)
else:
# advance to the empty slice starting at position n
next(islice(iterator, n, n), None)
defnth(iterable, n, default=None):
"Returnsthe nth item or a default value"
returnnext(islice(iterable, n, None), default)
defall_equal(iterable):
"ReturnsTrue if all the elements are equal to each other"
g = groupby(iterable)
returnnext(g, True) andnotnext(g, False)
defquantify(iterable, pred=bool):
"Counthow many times the predicate is true"
returnsum(map(pred, iterable))
defpadnone(iterable):
"""Returns the sequence elements and then returns Noneindefinitely.
Useful for emulating the behavior of thebuilt-in map() function.
"""
return chain(iterable, repeat(None))
defncycles(iterable, n):
"Returnsthe sequence elements n times"
return chain.from_iterable(repeat(tuple(iterable), n))
defdotproduct(vec1, vec2):
returnsum(map(operator.mul, vec1, vec2))
defflatten(listOfLists):
"Flattenone level of nesting"
return chain.from_iterable(listOfLists)
defrepeatfunc(func, times=None, *args):
"""Repeat calls to func with specified arguments.
Example: repeatfunc(random.random)
"""
if times isNone:
return starmap(func, repeat(args))
return starmap(func, repeat(args, times))
defpairwise(iterable):
"s-> (s0,s1), (s1,s2), (s2, s3), ..."
a, b = tee(iterable)
next(b, None)
returnzip(a, b)
defgrouper(iterable, n, fillvalue=None):
"Collectdata into fixed-length chunks or blocks"
# grouper('ABCDEFG', 3, 'x') --> ABC DEF Gxx"
args = [iter(iterable)] * n
return zip_longest(*args, fillvalue=fillvalue)
defroundrobin(*iterables):
"roundrobin('ABC','D', 'EF') --> A D E B F C"
# Recipe credited to George Sakkis
num_active =len(iterables)
nexts = cycle(iter(it).__next__ for it in iterables)
while num_active:
try:
fornextin nexts:
yieldnext()
exceptStopIteration:
# Remove the iterator we just exhausted from the cycle.
num_active -=1
nexts = cycle(islice(nexts, num_active))
defpartition(pred, iterable):
'Use apredicate to partition entries into false entries and true entries'
# partition(is_odd, range(10)) --> 0 2 4 6 8 and 13 5 7 9
t1, t2 = tee(iterable)
return filterfalse(pred, t1), filter(pred, t2)
defpowerset(iterable):
"powerset([1,2,3])--> () (1,) (2,) (3,) (1,2) (1,3) (2,3) (1,2,3)"
s =list(iterable)
return chain.from_iterable(combinations(s,r) for r inrange(len(s)+1))
defunique_everseen(iterable, key=None):
"Listunique elements, preserving order. Remember all elements ever seen."
# unique_everseen('AAAABBBCCDAABBB') --> A B C D
# unique_everseen('ABBCcAD', str.lower) --> A B C D
seen =set()
seen_add = seen.add
if key isNone:
for element in filterfalse(seen.__contains__, iterable):
seen_add(element)
yield element
else:
for element in iterable:
k = key(element)
if k notin seen:
seen_add(k)
yield element
defunique_justseen(iterable, key=None):
"Listunique elements, preserving order. Remember only the element just seen."
# unique_justseen('AAAABBBCCDAABBB') --> A B C D A B
# unique_justseen('ABBCcAD', str.lower) --> A B C A D
returnmap(next, map(itemgetter(1), groupby(iterable, key)))
defiter_except(func, exception, first=None):
""" Call a function repeatedly until an exception israised.
Converts a call-until-exception interfaceto an iterator interface.
Like builtins.iter(func, sentinel) but usesan exception instead
of a sentinel to end the loop.
Examples:
iter_except(functools.partial(heappop,h), IndexError) # priority queueiterator
iter_except(d.popitem, KeyError) # non-blocking dict iterator
iter_except(d.popleft, IndexError) # non-blocking dequeiterator
iter_except(q.get_nowait,Queue.Empty) # loopover a producer Queue
iter_except(s.pop, KeyError) # non-blocking setiterator
"""
try:
if first isnotNone:
yield first() # For database APIs needing an initial cast to db.first()
whileTrue:
yield func()
except exception:
pass
deffirst_true(iterable, default=False, pred=None):
"""Returns the first true value in the iterable.
If no true value is found, returns*default*
If *pred* is not None, returns the firstitem
for which pred(item) is true.
"""
# first_true([a,b,c], x) --> a or b or c or x
# first_true([a,b], x, f) --> a if f(a) else b if f(b) else x
returnnext(filter(pred, iterable), default)
defrandom_product(*args, repeat=1):
"Randomselection from itertools.product(*args, **kwds)"
pools = [tuple(pool) for pool in args] * repeat
returntuple(random.choice(pool) for pool in pools)
defrandom_permutation(iterable, r=None):
"Randomselection from itertools.permutations(iterable, r)"
pool =tuple(iterable)
r =len(pool) if r isNoneelse r
returntuple(random.sample(pool, r))
defrandom_combination(iterable, r):
"Randomselection from itertools.combinations(iterable, r)"
pool =tuple(iterable)
n =len(pool)
indices =sorted(random.sample(range(n), r))
returntuple(pool[i] for i in indices)
defrandom_combination_with_replacement(iterable, r):
"Randomselection from itertools.combinations_with_replacement(iterable, r)"
pool =tuple(iterable)
n =len(pool)
indices =sorted(random.randrange(n) for i inrange(r))
returntuple(pool[i] for i in indices)
defnth_combination(iterable, r, index):
'Equivalentto list(combinations(iterable, r))[index]'
pool =tuple(iterable)
n =len(pool)
if r <0or r > n:
raiseValueError
c =1
k =min(r, n-r)
for i inrange(1, k+1):
c = c * (n - k + i) // i
if index <0:
index += c
if index <0or index >= c:
raiseIndexError
result = []
while r:
c, n, r = c*r//n, n-1, r-1
while index >= c:
index -= c
c, n = c*(n-r)//n, n-1
result.append(pool[-1-n])
returntuple(result)
注意,上述的很多代码都可以通过用本地变量来取代全局变量以获得更好的性能。例如,dotproduct方法可以被写作:
defdotproduct(vec1, vec2,sum=sum,map=map, mul=operator.mul):
returnsum(map(mul, vec1, vec2))