Pythonitertools为高效循环而创建迭代器的函数

python lib

本模块实现一系列 iterator ,这些迭代器受到APL,Haskell和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)-->1011121314...

cycle()

p

p0, p1, ... plast, p0, p1, ...

cycle('ABCD')-->ABCDABCD...

repeat()

elem [,n]

elem, elem, elem, ... 重复无限次或n次

repeat(10,3)-->101010

根据最短输入序列长度停止的迭代器:

迭代器

实参

结果

示例

accumulate()

p [,func]

p0, p0+p1, p0+p1+p2, ...

accumulate([1,2,3,4,5])-->1361015

chain()

p, q, ...

p0, p1, ... plast, q0, q1, ...

chain('ABC','DEF')-->ABCDEF

chain.from_iterable()

iterable -- 可迭代对象

p0, p1, ... plast, q0, q1, ...

chain.from_iterable(['ABC','DEF'])-->ABCDEF

compress()

data, selectors

(d[0] if s[0]), (d[1] if s[1]), ...

compress('ABCDEF',[1,0,1,0,1,1])-->ACEF

dropwhile()

pred, seq

seq[n], seq[n+1], ... 从pred首次真值测试失败开始

dropwhile(lambdax:x<5,[1,4,6,4,1])-->641

filterfalse()

pred, seq

seq中pred(x)为假值的元素,x是seq中的元素。

filterfalse(lambdax:x%2,range(10))-->02468

groupby()

iterable[, key]

根据key(v)值分组的迭代器

islice()

seq, [start,] stop [, step]

seq[start:stop:step]中的元素

islice('ABCDEFG',2,None)-->CDEFG

starmap()

func, seq

func(*seq[0]), func(*seq[1]), ...

starmap(pow,[(2,5),(3,2),(10,3)])-->3291000

takewhile()

pred, seq

seq[0], seq[1], ..., 直到pred真值测试失败

takewhile(lambdax:x<5,[1,4,6,4,1])-->14

tee()

it, n

it1, it2, ... itn 将一个迭代器拆分为n个迭代器

zip_longest()

p, q, ...

(p[0], q[0]), (p[1], q[1]), ...

zip_longest('ABCD','xy',fillvalue='-')-->AxByC-D-

排列组合迭代器:

迭代器

实参

结果

product()

p, q, ... [repeat=1]

笛卡尔积,相当于嵌套的for循环

permutations()

p[, r]

长度r元组,所有可能的排列,无重复元素

combinations()

p, r

长度r元组,有序,无重复元素

combinations_with_replacement()

p, r

长度r元组,有序,元素可重复

product('ABCD',repeat=2)

AAABACADBABBBCBDCACBCCCDDADBDCDD

permutations('ABCD',2)

ABACADBABCBDCACBCDDADBDC

combinations('ABCD',2)

ABACADBCBDCD

combinations_with_replacement('ABCD',2)

AAABACADBBBCBDCCCDDD

Itertool函数¶

下列模块函数均创建并返回迭代器。有些迭代器不限制输出流长度,所以它们只应在能截断输出流的函数或循环中使用。

itertools.accumulate(iterable[, func])

创建一个迭代器,返回累加和或其他二元函数的累加结果(通过可选参数 func 指定)。如果提供了 func ,它应是2个参数的函数。输入 iterable 元素类型应是 func 能支持的任意类型。(例如,对于默认的加法操作,元素可以是任一支持加法的类型,包括 DecimalFraction )。如果可迭代对象的输入为空,输出也为空。

大致相当于:

python3 notranslate">
defaccumulate(iterable,func=operator.add):

'Return running 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

yieldtotal

forelementinit:

total=func(total,element)

yieldtotal

func 参数有几种用法。它可以被设为 min() 最终得到一个最小值,或者设为 max() 最终得到一个最大值,或设为 operator.mul() 最终得到一个乘积。摊销表可通过累加利息和支付款项得到。给iterable设置初始值并只将参数 func 设为累加总数可以对一阶 递归关系 建模。

python3 notranslate">
>>> 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]

# Amortize a 5% loan of 1000 with 4 annual payments of 90

>>> cashflows=[1000,-90,-90,-90,-90]

>>> list(accumulate(cashflows,lambdabal,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=lambdax,_:r*x*(1-x)

>>> r=3.8

>>> x0=0.4

>>> inputs=repeat(x0,36)# only the initial value is used

>>> [format(x,'.2f')forxinaccumulate(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() ,它只返回一个最终累积值。

3.2 新版功能.

在 3.3 版更改: 增加可选参数 func

itertools.chain(*iterables)

创建一个迭代器,它首先返回第一个可迭代对象中所有元素,接着返回下一个可迭代对象中所有元素,直到耗尽所有可迭代对象中的元素。可将多个序列处理为单个序列。大致相当于:

defchain(*iterables):

# chain('ABC', 'DEF') --> A B C D E F

foritiniterables:

forelementinit:

yieldelement

classmethod chain.from_iterable(iterable)

构建类似 chain() 迭代器的另一个选择。从一个单独的可迭代参数中得到链式输入,该参数是延迟计算的。大致相当于:

deffrom_iterable(iterables):

# chain.from_iterable(['ABC', 'DEF']) --> A B C D E F

foritiniterables:

forelementinit:

yieldelement

itertools.combinations(iterable, r)

返回由输入 iterable 中元素组成长度为 r 的子序列。

组合按照字典序返回。所以如果输入 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)

ifr>n:

return

indices=list(range(r))

yieldtuple(pool[i]foriinindices)

whileTrue:

foriinreversed(range(r)):

ifindices[i]!=i+n-r:

break

else:

return

indices[i]+=1

forjinrange(i+1,r):

indices[j]=indices[j-1]+1

yieldtuple(pool[i]foriinindices)

combinations() 的代码可被改写为 permutations() 过滤后的子序列,(相对于元素在输入中的位置)元素不是有序的。

defcombinations(iterable,r):

pool=tuple(iterable)

n=len(pool)

forindicesinpermutations(range(n),r):

ifsorted(indices)==list(indices):

yieldtuple(pool[i]foriinindices)

0<=r<=n 时,返回项的个数是 n!/r!/(n-r)!;当 r>n 时,返回项个数为0。

itertools.combinations_with_replacement(iterable, r)

返回由输入 iterable 中元素组成的长度为 r 的子序列,允许每个元素可重复出现。

组合按照字典序返回。所以如果输入 iterable 是有序的,生成的组合元组也是有序的。

不同位置的元素是不同的,即使它们的值相同。因此如果输入中的元素都是不同的话,返回的组合中元素也都会不同。

大致相当于:

defcombinations_with_replacement(iterable,r):

# combinations_with_replacement('ABC', 2) --> AA AB AC BB BC CC

pool=tuple(iterable)

n=len(pool)

ifnotnandr:

return

indices=[0]*r

yieldtuple(pool[i]foriinindices)

whileTrue:

foriinreversed(range(r)):

ifindices[i]!=n-1:

break

else:

return

indices[i:]=[indices[i]+1]*(r-i)

yieldtuple(pool[i]foriinindices)

combinations_with_replacement() 的代码可被改写为 production() 过滤后的子序列,(相对于元素在输入中的位置)元素不是有序的。

defcombinations_with_replacement(iterable,r):

pool=tuple(iterable)

n=len(pool)

forindicesinproduct(range(n),repeat=r):

ifsorted(indices)==list(indices):

yieldtuple(pool[i]foriinindices)

n>0 时,返回项个数为 (n+r-1)!/r!/(n-1)!.

3.1 新版功能.

itertools.compress(data, selectors)

创建一个迭代器,它返回 data 中经 selectors 真值测试为 True 的元素。迭代器在两者较短的长度处停止。大致相当于:

defcompress(data,selectors):

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

return(dford,sinzip(data,selectors)ifs)

3.1 新版功能.

itertools.count(start=0, step=1)

创建一个迭代器,它从 start 值开始,返回均匀间隔的值。常用于 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:

yieldn

n+=step

当对浮点数计数时,替换为乘法代码有时精度会更好,例如: (start+step*iforiincount())

在 3.1 版更改: 增加参数 step ,允许非整型。

itertools.cycle(iterable)

创建一个迭代器,返回 iterable 中所有元素并保存一个副本。当取完 iterable 中所有元素,返回副本中的所有元素。无限重复。大致相当于:

defcycle(iterable):

# cycle('ABCD') --> A B C D A B C D A B C D ...

saved=[]

forelementiniterable:

yieldelement

saved.append(element)

whilesaved:

forelementinsaved:

yieldelement

注意,该函数可能需要相当大的辅助空间(取决于 iterable 的长度)。

itertools.dropwhile(predicate, iterable)

创建一个迭代器,如果 predicate 为true,迭代器丢弃这些元素,然后返回其他元素。注意,迭代器在 predicate 首次为false之前不会产生任何输出,所以可能需要一定长度的启动时间。大致相当于:

defdropwhile(predicate,iterable):

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

iterable=iter(iterable)

forxiniterable:

ifnotpredicate(x):

yieldx

break

forxiniterable:

yieldx

itertools.filterfalse(predicate, iterable)

创建一个迭代器,只返回 iterablepredicateFalse 的元素。如果 predicateNone,返回真值测试为false的元素。大致相当于:

deffilterfalse(predicate,iterable):

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

ifpredicateisNone:

predicate=bool

forxiniterable:

ifnotpredicate(x):

yieldx

itertools.groupby(iterable, key=None)

创建一个迭代器,返回 iterable 中连续的键和组。key 是一个计算元素键值函数。如果未指定或为 Nonekey 缺省为恒等函数(identity function),返回元素不变。一般来说,iterable 需用同一个键值函数预先排序。

groupby() 操作类似于Unix中的 uniq。当每次 key 函数产生的键值改变时,迭代器会分组或生成一个新组(这就是为什么通常需要使用同一个键值函数先对数据进行排序)。这种行为与SQL的GROUP BY操作不同,SQL的操作会忽略输入的顺序将相同键值的元素分在同组中。

返回的组本身也是一个迭代器,它与 groupby() 共享底层的可迭代对象。因为源是共享的,当 groupby() 对象向后迭代时,前一个组将消失。因此如果稍后还需要返回结果,可保存为列表:

groups=[]

uniquekeys=[]

data=sorted(data,key=keyfunc)

fork,gingroupby(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):

ifkeyisNone:

key=lambdax:x

self.keyfunc=key

self.it=iter(iterable)

self.tgtkey=self.currkey=self.currvalue=object()

def__iter__(self):

returnself

def__next__(self):

self.id=object()

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,self.id))

def_grouper(self,tgtkey,id):

whileself.idisidandself.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 不是0,跳过 iterable 中的元素,直到到达 start 这个位置。之后迭代器连续返回元素,除非 step 设置的值很高导致被跳过。如果 stopNone,迭代器耗光为止;否则,在指定的位置停止。与普通的切片不同,islice() 不支持将 startstop ,或 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.stoporsys.maxsize,s.stepor1

it=iter(range(start,stop,step))

try:

nexti=next(it)

exceptStopIteration:

# Consume *iterable* up to the *start* position.

fori,elementinzip(range(start),iterable):

pass

return

try:

fori,elementinenumerate(iterable):

ifi==nexti:

yieldelement

nexti=next(it)

exceptStopIteration:

# Consume to *stop*.

fori,elementinzip(range(i+1,stop),iterable):

pass

如果 startNone,迭代从0开始。如果 stepNone ,步长缺省为1。

itertools.permutations(iterable, r=None)

连续返回由 iterable 元素生成长度为 r 的排列。

如果 r 未指定或为 Noner 默认设置为 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=nifrisNoneelser

ifr>n:

return

indices=list(range(n))

cycles=list(range(n,n-r,-1))

yieldtuple(pool[i]foriinindices[:r])

whilen:

foriinreversed(range(r)):

cycles[i]-=1

ifcycles[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]foriinindices[:r])

break

else:

return

permutations() 的代码也可被改写为 product() 的子序列,只要将含有重复元素(来自输入中同一位置的)的项排除。

defpermutations(iterable,r=None):

pool=tuple(iterable)

n=len(pool)

r=nifrisNoneelser

forindicesinproduct(range(n),repeat=r):

iflen(set(indices))==r:

yieldtuple(pool[i]foriinindices)

0<=r<=n ,返回项个数为 n!/(n-r)! ;当 r>n ,返回项个数为0。

itertools.product(*iterables, repeat=1)

可迭代对象输入的笛卡儿积。

大致相当于生成器表达式中的嵌套循环。例如, product(A,B)((x,y)forxinAforyinB) 返回结果一样。

嵌套循环像里程表那样循环变动,每次迭代时将最右侧的元素向后迭代。这种模式形成了一种字典序,因此如果输入的可迭代对象是已排序的,笛卡尔积元组依次序发出。

要计算可迭代对象自身的笛卡尔积,将可选参数 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)forpoolinargs]*repeat

result=[[]]

forpoolinpools:

result=[x+[y]forxinresultforyinpool]

forprodinresult:

yieldtuple(prod)

itertools.repeat(object[, times])

创建一个迭代器,不断重复 object 。除非设定参数 times ,否则将无限重复。可用于 map() 函数中的参数,被调用函数可得到一个不变参数。也可用于 zip() 的参数以在元组记录中创建一个不变的部分。

大致相当于:

defrepeat(object,times=None):

# repeat(10, 3) --> 10 10 10

iftimesisNone:

whileTrue:

yieldobject

else:

foriinrange(times):

yieldobject

repeat 最常见的用途就是在 mapzip 提供一个常量流:

>>> list(map(pow,range(10),repeat(2)))

[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

itertools.starmap(function, iterable)

创建一个迭代器,使用从可迭代对象中获取的参数来计算该函数。当参数对应的形参已从一个单独可迭代对象组合为元组时(数据已被“预组对”)可用此函数代替 map()map()starmap() 之间的区别可以类比 function(a,b)function(*c) 的区别。大致相当于:

defstarmap(function,iterable):

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

forargsiniterable:

yieldfunction(*args)

itertools.takewhile(predicate, iterable)

创建一个迭代器,只要 predicate 为真就从可迭代对象中返回元素。大致相当于:

deftakewhile(predicate,iterable):

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

forxiniterable:

ifpredicate(x):

yieldx

else:

break

itertools.tee(iterable, n=2)

从一个可迭代对象中返回 n 个独立的迭代器。

下面的Python代码能帮助解释 tee 做了什么(尽管实际的实现更复杂,而且仅使用了一个底层的 FIFO 队列)。

大致相当于:

deftee(iterable,n=2):

it=iter(iterable)

deques=[collections.deque()foriinrange(n)]

defgen(mydeque):

whileTrue:

ifnotmydeque:# when the local deque is empty

try:

newval=next(it)# fetch a new value and

exceptStopIteration:

return

fordindeques:# load it to all the deques

d.append(newval)

yieldmydeque.popleft()

returntuple(gen(d)fordindeques)

一旦 tee() 实施了一次分裂,原有的 iterable 不应再被使用;否则tee对象无法得知 iterable 可能已向后迭代。

tee 迭代器不是线程安全的。当同时使用由同一个 tee() 调用所返回的迭代器时可能引发 RuntimeError,即使原本的 iterable 是线程安全的。

该迭代工具可能需要相当大的辅助存储空间(这取决于要保存多少临时数据)。通常,如果一个迭代器在另一个迭代器开始之前就要使用大部份或全部数据,使用 list() 会比 tee() 更快。

itertools.zip_longest(*iterables, fillvalue=None)

创建一个迭代器,从每个可迭代对象中收集元素。如果可迭代对象的长度未对齐,将根据 fillvalue 填充缺失值。迭代持续到耗光最长的可迭代对象。大致相当于:

defzip_longest(*args,fillvalue=None):

# zip_longest('ABCD', 'xy', fillvalue='-') --> Ax By C- D-

iterators=[iter(it)foritinargs]

num_active=len(iterators)

ifnotnum_active:

return

whileTrue:

values=[]

fori,itinenumerate(iterators):

try:

value=next(it)

exceptStopIteration:

num_active-=1

ifnotnum_active:

return

iterators[i]=repeat(fillvalue)

value=fillvalue

values.append(value)

yieldtuple(values)

如果其中一个可迭代对象有无限长度,zip_longest() 函数应封装在限制调用次数的场景中(例如 islice()takewhile())。除非指定, fillvalue 默认为 None

Itertools食谱¶

本节将展示如何使用现有的itertools作为基础构件来创建扩展的工具集。

扩展的工具提供了与底层工具集相同的高性能。保持了超棒的内存利用率,因为一次只处理一个元素,而不是将整个可迭代对象加载到内存。代码量保持得很小,以函数式风格将这些工具连接在一起,有助于消除临时变量。速度依然很快,因为倾向于使用“矢量化”构件来取代解释器开销大的 for 循环和 generator

deftake(n,iterable):

"Return first n items of the iterable as a list"

returnlist(islice(iterable,n))

defprepend(value,iterator):

"Prepend a single value in front of an iterator"

# prepend(1, [2, 3, 4]) -> 1 2 3 4

returnchain([value],iterator)

deftabulate(function,start=0):

"Return function(0), function(1), ..."

returnmap(function,count(start))

deftail(n,iterable):

"Return an iterator over the last n items"

# tail(3, 'ABCDEFG') --> E F G

returniter(collections.deque(iterable,maxlen=n))

defconsume(iterator,n=None):

"Advance the iterator n-steps ahead. If n is None, consume entirely."

# Use functions that consume iterators at C speed.

ifnisNone:

# 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):

"Returns the nth item or a default value"

returnnext(islice(iterable,n,None),default)

defall_equal(iterable):

"Returns True if all the elements are equal to each other"

g=groupby(iterable)

returnnext(g,True)andnotnext(g,False)

defquantify(iterable,pred=bool):

"Count how many times the predicate is true"

returnsum(map(pred,iterable))

defpadnone(iterable):

"""Returns the sequence elements and then returns None indefinitely.

Useful for emulating the behavior of the built-in map() function.

"""

returnchain(iterable,repeat(None))

defncycles(iterable,n):

"Returns the sequence elements n times"

returnchain.from_iterable(repeat(tuple(iterable),n))

defdotproduct(vec1,vec2):

returnsum(map(operator.mul,vec1,vec2))

defflatten(listOfLists):

"Flatten one level of nesting"

returnchain.from_iterable(listOfLists)

defrepeatfunc(func,times=None,*args):

"""Repeat calls to func with specified arguments.

Example: repeatfunc(random.random)

"""

iftimesisNone:

returnstarmap(func,repeat(args))

returnstarmap(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):

"Collect data into fixed-length chunks or blocks"

# grouper('ABCDEFG', 3, 'x') --> ABC DEF Gxx"

args=[iter(iterable)]*n

returnzip_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__foritiniterables)

whilenum_active:

try:

fornextinnexts:

yieldnext()

exceptStopIteration:

# Remove the iterator we just exhausted from the cycle.

num_active-=1

nexts=cycle(islice(nexts,num_active))

defpartition(pred,iterable):

'Use a predicate to partition entries into false entries and true entries'

# partition(is_odd, range(10)) --> 0 2 4 6 8 and 1 3 5 7 9

t1,t2=tee(iterable)

returnfilterfalse(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)

returnchain.from_iterable(combinations(s,r)forrinrange(len(s)+1))

defunique_everseen(iterable,key=None):

"List unique 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

ifkeyisNone:

forelementinfilterfalse(seen.__contains__,iterable):

seen_add(element)

yieldelement

else:

forelementiniterable:

k=key(element)

ifknotinseen:

seen_add(k)

yieldelement

defunique_justseen(iterable,key=None):

"List unique 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 is raised.

Converts a call-until-exception interface to an iterator interface.

Like builtins.iter(func, sentinel) but uses an exception instead

of a sentinel to end the loop.

Examples:

iter_except(functools.partial(heappop, h), IndexError) # priority queue iterator

iter_except(d.popitem, KeyError) # non-blocking dict iterator

iter_except(d.popleft, IndexError) # non-blocking deque iterator

iter_except(q.get_nowait, Queue.Empty) # loop over a producer Queue

iter_except(s.pop, KeyError) # non-blocking set iterator

"""

try:

iffirstisnotNone:

yieldfirst()# For database APIs needing an initial cast to db.first()

whileTrue:

yieldfunc()

exceptexception:

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 first item

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):

"Random selection from itertools.product(*args, **kwds)"

pools=[tuple(pool)forpoolinargs]*repeat

returntuple(random.choice(pool)forpoolinpools)

defrandom_permutation(iterable,r=None):

"Random selection from itertools.permutations(iterable, r)"

pool=tuple(iterable)

r=len(pool)ifrisNoneelser

returntuple(random.sample(pool,r))

defrandom_combination(iterable,r):

"Random selection from itertools.combinations(iterable, r)"

pool=tuple(iterable)

n=len(pool)

indices=sorted(random.sample(range(n),r))

returntuple(pool[i]foriinindices)

defrandom_combination_with_replacement(iterable,r):

"Random selection from itertools.combinations_with_replacement(iterable, r)"

pool=tuple(iterable)

n=len(pool)

indices=sorted(random.randrange(n)foriinrange(r))

returntuple(pool[i]foriinindices)

defnth_combination(iterable,r,index):

'Equivalent to list(combinations(iterable, r))[index]'

pool=tuple(iterable)

n=len(pool)

ifr<0orr>n:

raiseValueError

c=1

k=min(r,n-r)

foriinrange(1,k+1):

c=c*(n-k+i)//i

ifindex<0:

index+=c

ifindex<0orindex>=c:

raiseIndexError

result=[]

whiler:

c,n,r=c*r//n,n-1,r-1

whileindex>=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))

以上是 Pythonitertools为高效循环而创建迭代器的函数 的全部内容, 来源链接: utcz.com/z/508415.html

回到顶部