itertools

itertools — Functions creating iterators for efficient looping

2.3版本的新功能。

该模块实现了许多受APL,Haskell和SML构造启发的迭代器构建块。每一个都以适合Python的形式进行了重新编排。

该模块标准化了一套核心快速,高效的内存工具,这些工具本身或其组合都很有用。它们一起构成了一个“迭代器代数”,可以在纯Python中简洁高效地构建专用工具。

例如,SML提供了一个制表工具:tabulate(f)它产生一个序列f(0), f(1), ...。通过组合imap()count()形成,Python可以达到同样的效果imap(f, count())

这些工具及其内置对应模块也可以很好地处理operator模块中的高速功能。例如,可以将乘法运算符映射到两个向量上以形成高效的点积:sum(imap(operator.mul, vector1, vector2))

无限迭代器:

IteratorArgumentsResultsExample
count()start, stepstart, start+step, start+2*step, ...count(10) --> 10 11 12 13 14 ...
cycle()pp0, p1, ... plast, p0, p1, ...cycle('ABCD') --> A B C D A B C D ...
repeat()elem ,nelem, elem, elem, ... endlessly or up to n timesrepeat(10, 3) --> 10 10 10

迭代器终止于最短的输入序列:

IteratorArgumentsResultsExample
chain()p, q, ...p0, p1, ... plast, q0, q1, ...chain('ABC', 'DEF') --> A B C D E F
compress()data, selectors(d0 if s0), (d1 if s1), ...compress('ABCDEF', 1,0,1,0,1,1) --> A C E F
dropwhile()pred, seqseqn, seqn+1, starting when pred failsdropwhile(lambda x: x<5, 1,4,6,4,1) --> 6 4 1
groupby()iterable, keyfuncsub-iterators grouped by value of keyfunc(v)
ifilter()pred, seqelements of seq where pred(elem) is trueifilter(lambda x: x%2, range(10)) --> 1 3 5 7 9
ifilterfalse()pred, seqelements of seq where pred(elem) is falseifilterfalse(lambda x: x%2, range(10)) --> 0 2 4 6 8
islice()seq, start, stop , stepelements from seqstart:stop:stepislice('ABCDEFG', 2, None) --> C D E F G
imap()func, p, q, ...func(p0, q0), func(p1, q1), ...imap(pow, (2,3,10), (5,2,3)) --> 32 9 1000
starmap()func, seqfunc(*seq0), func(*seq1), ...starmap(pow, (2,5), (3,2), (10,3)) --> 32 9 1000
tee()it, nit1, it2, ... itn splits one iterator into n
takewhile()pred, seqseq0, seq1, until pred failstakewhile(lambda x: x<5, 1,4,6,4,1) --> 1 4
izip()p, q, ...(p0, q0), (p1, q1), ...izip('ABCD', 'xy') --> Ax By
izip_longest()p, q, ...(p0, q0), (p1, q1), ...izip_longest('ABCD', 'xy', fillvalue='-') --> Ax By C- D-

组合发生器:

IteratorArgumentsResults
product()p, q, ... repeat=1cartesian product, equivalent to a nested for-loop
permutations()p, rr-length tuples, all possible orderings, no repeated elements
combinations()p, rr-length tuples, in sorted order, no repeated elements
combinations_with_replacement()p, rr-length tuples, in sorted order, with repeated elements
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

1. Itertool功能

以下模块函数都构造并返回迭代器。一些提供无限长度的流,所以它们只能由截断流的函数或循环访问。

itertools.chain(*iterables)

创建一个迭代器,它从第一个迭代器返回元素,直到它耗尽,然后继续下一个迭代器,直到所有迭代器都耗尽。用于将连续序列作为单个序列进行处理。大致相当于:

def chain(*iterables): # chain('ABC', 'DEF') --> A B C D E F for it in iterables: for element in it: yield element

classmethod chain.from_iterable(iterable)

替代构造函数chain()。从单个迭代参数中获取链接的输入,这个参数是懒惰评估的。大致相当于:

def from_iterable(iterables): # chain.from_iterable(['ABC', 'DEF']) --> A B C D E F for it in iterables: for element in it: yield element

2.6版本中的新功能。

itertools.combinations(iterable, r)

从输入迭代中返回元素的r长度子序列。

组合按字典顺序排列。所以,如果输入迭代被排序,组合元组将按排序顺序生成。

元素根据他们的位置被视为唯一的,而不是他们的价值。因此,如果输入元素是唯一的,则每个组合中都不会有重复值。

大致相当于:

def combinations(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 = range(r) yield tuple(pool[i] for i in indices) while True: for i in reversed(range(r)): if indices[i] != i + n - r: break else: return indices[i] += 1 for j in range(i+1, r): indices[j] = indices[j-1] + 1 yield tuple(pool[i] for i in indices)

代码for combinations()也可以表示为permutations()过滤条件后的子序列,其中元素不是按排序顺序(根据它们在输入池中的位置):

def combinations(iterable, r): pool = tuple(iterable) n = len(pool) for indices in permutations(range(n), r): if sorted(indices) == list(indices): yield tuple(pool[i] for i in indices)

返回的项目数是n! / r! / (n-r)!何时0 <= r <= n或何时为零r > n。

2.6版本中的新功能。

itertools.combinations_with_replacement(iterable, r)

从输入迭代中返回元素的r长度子序列,允许单个元素重复多次。

组合按字典顺序排列。所以,如果输入迭代被排序,组合元组将按排序顺序生成。

元素根据他们的位置被视为唯一的,而不是他们的价值。因此,如果输入元素是唯一的,则生成的组合也将是唯一的。

大致相当于:

def combinations_with_replacement(iterable, r): # combinations_with_replacement('ABC', 2) --> AA AB AC BB BC CC pool = tuple(iterable) n = len(pool) if not n and r: return indices = [0] * r yield tuple(pool[i] for i in indices) while True: for i in reversed(range(r)): if indices[i] != n - 1: break else: return indices[i:] = [indices[i] + 1] * (r - i) yield tuple(pool[i] for i in indices)

combinations_with_replacement()也可以表示为product()过滤条件后的子序列,其中元素不是按排序顺序(根据它们在输入池中的位置):

def combinations_with_replacement(iterable, r): pool = tuple(iterable) n = len(pool) for indices in product(range(n), repeat=r): if sorted(indices) == list(indices): yield tuple(pool[i] for i in indices)

返回的项目数是(n+r-1)! / r! / (n-1)!何时n > 0。

2.7版本的新功能。

itertools.compress(data, selectors)

创建一个迭代,用于过滤来自数据的元素,仅返回那些在选择器中具有相应元素的计算结果True数据选择器迭代耗尽时停止。大致相当于:

def compress(data, selectors): # compress('ABCDEF', [1,0,1,0,1,1]) --> A C E F return (d for d, s in izip(data, selectors) if s)

2.7版本的新功能。

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

创建一个迭代器,返回从n开始的均匀间隔值。通常用作参数imap()来生成连续的数据点。此外,用于izip()添加序列号。相当于:

def count(start=0, step=1): # count(10) --> 10 11 12 13 14 ... # count(2.5, 0.5) -> 2.5 3.0 3.5 ... n = start while True: yield n n += step

使用浮点数进行计数时,有时可以通过替换乘法代码来实现更高的精度,例如:(start + step * i for i in count())

在版本2.7中更改:添加了步骤参数并允许使用非整数参数。

itertools.cycle(iterable)

使迭代器从迭代器中返回元素并保存每个元素的副本。当迭代器耗尽时,从保存的副本中返回元素。重复无限期地。大致相当于:

def cycle(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)

只要谓词为真,就创建一个从迭代器中删除元素的迭代器; 之后,返回每个元素。请注意,只有谓词首先变为false时,迭代器才会产生任何输出,因此可能会有较长的启动时间。大致相当于:

def dropwhile(predicate, iterable): # dropwhile(lambda x: x<5, [1,4,6,4,1]) --> 6 4 1 iterable = iter(iterable) for x in iterable: if not predicate(x): yield x break for x in iterable: yield x

itertools.groupby(iterable[, key])

创建一个从迭代器中返回连续和组的迭代器。的关键是计算每个元素的密钥值的函数。如果未指定或是None默认为恒等函数,并返回元件不变。一般来说,迭代器需要在同一个关键函数上进行排序。

其操作groupby()uniqUnix中的过滤器类似。每当关键函数的值发生变化时它就会生成一个中断或新的组(这就是为什么通常需要使用相同的关键函数对数据进行排序的原因)。该行为与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() 大致相当于:

class groupby(object): # [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 is None: key = lambda x: x self.keyfunc = key self.it = iter(iterable) self.tgtkey = self.currkey = self.currvalue = object() def __iter__(self): return self def next(self): while self.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): while self.currkey == tgtkey: yield self.currvalue self.currvalue = next(self.it) # Exit on StopIteration self.currkey = self.keyfunc(self.currvalue)

2.4版本中的新功能。

itertools.ifilter(predicate, iterable)

创建一个迭代器,用于过滤来自iterable的元素,仅返回谓词所针对的元素True。如果谓词None,则返回true的项目。大致相当于:

def ifilter(predicate, iterable): # ifilter(lambda x: x%2, range(10)) --> 1 3 5 7 9 if predicate is None: predicate = bool for x in iterable: if predicate(x): yield x

itertools.ifilterfalse(predicate, iterable)

创建一个迭代器,用于过滤来自iterable的元素,仅返回谓词所针对的元素False。如果谓词None,则返回false的项目。大致相当于:

def ifilterfalse(predicate, iterable): # ifilterfalse(lambda x: x%2, range(10)) --> 0 2 4 6 8 if predicate is None: predicate = bool for x in iterable: if not predicate(x): yield x

itertools.imap(function, *iterables)

制作一个迭代器,使用来自每个迭代器的参数计算函数。如果函数设置为None,则将imap()参数作为元组返回。喜欢map()但是当最短迭代耗尽而不是填充None较短的迭代时停止。不同的原因是无限的迭代器参数通常是错误的map()(因为输出是完全评估的),但是代表了提供参数的常见和有用的方法imap()。大致相当于:

def imap(function, *iterables): # imap(pow, (2,3,10), (5,2,3)) --> 32 9 1000 iterables = map(iter, iterables) while True: args = [next(it) for it in iterables] if function is None: yield tuple(args) else: yield function(*args)

itertools.islice(iterable, stop)itertools.islice(iterable, start, stop[, step])

创建一个从迭代器中返回选定元素的迭代器。如果start不为零,则跳过迭代器中的元素直到达到起始点。之后,元素将被连续返回,除非将步骤设置为高于导致项目被跳过的元素。如果停止None,那么迭代继续进行,直到迭代器被耗尽,如果在所有; 否则,它停在指定位置。与常规切片不同,islice()不支持开始停止步骤的负值。可用于从内部结构扁平化的数据中提取相关字段(例如,多行报告可在每三行中列出名称字段)。大致相当于:

def islice(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) it = iter(xrange(s.start or 0, s.stop or sys.maxint, s.step or 1)) nexti = next(it) for i, element in enumerate(iterable): if i == nexti: yield element nexti = next(it)

如果开始None,然后重复从零开始。如果步骤None,则步骤默认为1。

在版本2.5中更改:接受None默认启动步骤的值。

itertools.izip(*iterables)

创建一个迭代器,用于汇总来自每个迭代器的元素。就像zip()不同的是它返回一个迭代器,而不是一个列表。用于一次对多个迭代进行锁步迭代。大致相当于:

def izip(*iterables): # izip('ABCD', 'xy') --> Ax By iterators = map(iter, iterables) while iterators: yield tuple(map(next, iterators))

版本2.4中更改:当没有指定迭代项时,返回一个零长度迭代器而不是引发TypeError异常。

可保证迭代的从左到右的评估顺序。这使得使用一个将数据序列聚类为n长度组的成语成为可能izip(*[iter(s)]*n)

izip()只有当您不关心较长迭代中的尾随,不匹配值时才应使用不等长输入。如果这些值很重要,请izip_longest()改用它。

itertools.izip_longest(*iterables[, fillvalue])

创建一个迭代器,用于汇总来自每个迭代器的元素。如果迭代的长度不均匀,缺少的值将用fillvalue填充。迭代继续下去,直到最长的迭代耗尽。大致相当于:

class ZipExhausted(Exception): pass def izip_longest(*args, **kwds): # izip_longest('ABCD', 'xy', fillvalue='-') --> Ax By C- D- fillvalue = kwds.get('fillvalue') counter = [len(args) - 1] def sentinel(): if not counter[0]: raise ZipExhausted counter[0] -= 1 yield fillvalue fillers = repeat(fillvalue) iterators = [chain(it, sentinel(), fillers) for it in args] try: while iterators: yield tuple(map(next, iterators)) except ZipExhausted: pass

如果其中一个迭代可能是无限的,那么该izip_longest()函数应该包含限制调用次数的内容(例如islice()takewhile())。如果未指定,则fillvalue默认为None

2.6版本中的新功能。

itertools.permutations(iterable[, r])

返回迭代中元素的连续r长度排列。

如果未指定r或者rNone,则r默认为可迭代的长度,并且会生成所有可能的全长置换。

排列以词典排序顺序排列。所以,如果输入迭代被排序,排列元组将按排序顺序生成。

元素根据他们的位置被视为唯一的,而不是他们的价值。因此,如果输入元素是唯一的,则每个排列中都不会有重复值。

大致相当于:

def permutations(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 is None else r if r > n: return indices = range(n) cycles = range(n, n-r, -1) yield tuple(pool[i] for i in indices[:r]) while n: for i in reversed(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] yield tuple(pool[i] for i in indices[:r]) break else: return

该代码permutations()也可以表示为子序列product(),通过过滤排除具有重复元素的条目(来自输入池中相同位置的条目):

def permutations(iterable, r=None): pool = tuple(iterable) n = len(pool) r = n if r is None else r for indices in product(range(n), repeat=r): if len(set(indices)) == r: yield tuple(pool[i] for i in indices)

返回的项目数是n! / (n-r)!何时0 <= r <= n或何时为零r > n。

2.6版本中的新功能。

itertools.product(*iterables[, repeat])

输入迭代的笛卡尔乘积。

大致相当于生成器表达式中的嵌套for循环。例如,product(A, B)返回与((x,y) for x in A for y in B)

嵌套循环像一个里程表一样循环,最右边的元素在每次迭代中前进。这种模式创建了一个词典排序,因此如果输入的可迭代序列被排序,则产品元组将按排序顺序排列。

要计算与自身的迭代产品,请使用可选的repeat关键字参数指定重复次数。例如,product(A, repeat=4)意味着相同product(A, A, A, A)

这个函数大致等价于下面的代码,只是实际的实现不会在内存中建立中间结果:

def product(*args, **kwds): # 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 = map(tuple, args) * kwds.get('repeat', 1) result = [[]] for pool in pools: result = [x+[y] for x in result for y in pool] for prod in result: yield tuple(prod)

2.6版本中的新功能。

itertools.repeat(object[, times])

创建一个一遍又一遍地返回对象的迭代器。除非指定了times参数,否则无限期地运行。用作imap()不变函数参数的参数。还用于izip()在元组记录中创建常量字段。大致相当于:

def repeat(object, times=None): # repeat(10, 3) --> 10 10 10 if times is None: while True: yield object else: for i in xrange(times): yield object

常用的重复操作是向imapzip提供一个常量值的流:

>>> list(imap(pow, xrange(10), repeat(2))) [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

itertools.starmap(function, iterable)

创建一个迭代器,该迭代器使用从迭代器获得的参数来计算函数。用于替代imap()参数参数已经从单个迭代中分组的元组(数据已被“预先压缩”)。之间的差异imap()starmap()相似之处之间的区别function(a,b)function(*c)。大致相当于:

def starmap(function, iterable): # starmap(pow, [(2,5), (3,2), (10,3)]) --> 32 9 1000 for args in iterable: yield function(*args)

在版本2.6中更改:以前,starmap()要求函数参数为元组。现在,任何迭代都是允许的。

itertools.takewhile(predicate, iterable)

只要谓词为真,就创建一个从迭代器返回元素的迭代器。大致相当于:

def takewhile(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])

从一个迭代中返回n个独立的迭代器。大致相当于:

def tee(iterable, n=2): it = iter(iterable) deques = [collections.deque() for i in range(n)] def gen(mydeque): while True: if not mydeque: # when the local deque is empty newval = next(it) # fetch a new value and for d in deques: # load it to all the deques d.append(newval) yield mydeque.popleft() return tuple(gen(d) for d in deques)

一旦tee()进行了拆分,原始迭代器不应该用于其他任何地方; 否则,可以在没有通知T恤对象的情况下使迭代得以进行。

这个itertool可能需要大量的辅助存储(取决于需要存储多少临时数据)。一般来说,如果一个迭代器使用了大部分或全部数据的另一迭代开始前,它是更快地使用list()替代tee()

2.4版本中的新功能。

2. Recipes

本部分显示了使用现有itertools作为构建块来创建扩展工具集的配方。

扩展工具提供了与底层工具集相同的高性能。优越的内存性能是通过一次处理一个元素来保持的,而不是一次将整个迭代器放入内存中。通过将这些工具链接在一起,以一种功能性风格帮助消除临时变量,从而使代码量保持较小。通过使用for循环和发生器来优化 “矢量化”积木,从而保留高速度,这会导致解释器开销。

def take(n, iterable): "Return first n items of the iterable as a list" return list(islice(iterable, n)) def tabulate(function, start=0): "Return function(0), function(1), ..." return imap(function, count(start)) def consume(iterator, n): "Advance the iterator n-steps ahead. If n is none, consume entirely." # Use functions that consume iterators at C speed. if n is None: # 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) def nth(iterable, n, default=None): "Returns the nth item or a default value" return next(islice(iterable, n, None), default) def all_equal(iterable): "Returns True if all the elements are equal to each other" g = groupby(iterable) return next(g, True) and not next(g, False) def quantify(iterable, pred=bool): "Count how many times the predicate is true" return sum(imap(pred, iterable)) def padnone(iterable): """Returns the sequence elements and then returns None indefinitely. Useful for emulating the behavior of the built-in map() function. """ return chain(iterable, repeat(None)) def ncycles(iterable, n): "Returns the sequence elements n times" return chain.from_iterable(repeat(tuple(iterable), n)) def dotproduct(vec1, vec2): return sum(imap(operator.mul, vec1, vec2)) def flatten(listOfLists): "Flatten one level of nesting" return chain.from_iterable(listOfLists) def repeatfunc(func, times=None, *args): """Repeat calls to func with specified arguments. Example: repeatfunc(random.random) """ if times is None: return starmap(func, repeat(args)) return starmap(func, repeat(args, times)) def pairwise(iterable): "s -> (s0,s1), (s1,s2), (s2, s3), ..." a, b = tee(iterable) next(b, None) return izip(a, b) def grouper(iterable, n, fillvalue=None): "Collect data into fixed-length chunks or blocks" # grouper('ABCDEFG', 3, 'x') --> ABC DEF Gxx args = [iter(iterable)] * n return izip_longest(fillvalue=fillvalue, *args) def roundrobin(*iterables): "roundrobin('ABC', 'D', 'EF') --> A D E B F C" # Recipe credited to George Sakkis pending = len(iterables) nexts = cycle(iter(it).next for it in iterables) while pending: try: for next in nexts: yield next() except StopIteration: pending -= 1 nexts = cycle(islice(nexts, pending)) def powerset(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 in range(len(s)+1)) def unique_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 if key is None: for element in ifilterfalse(seen.__contains__, iterable): seen_add(element) yield element else: for element in iterable: k = key(element) if k not in seen: seen_add(k) yield element def unique_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 return imap(next, imap(itemgetter(1), groupby(iterable, key))) def iter_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 __builtin__.iter(func, sentinel) but uses an exception instead of a sentinel to end the loop. Examples: bsddbiter = iter_except(db.next, bsddb.error, db.first) heapiter = iter_except(functools.partial(heappop, h), IndexError) dictiter = iter_except(d.popitem, KeyError) dequeiter = iter_except(d.popleft, IndexError) queueiter = iter_except(q.get_nowait, Queue.Empty) setiter = iter_except(s.pop, KeyError) """ try: if first is not None: yield first() while 1: yield func() except exception: pass def random_product(*args, **kwds): "Random selection from itertools.product(*args, **kwds)" pools = map(tuple, args) * kwds.get('repeat', 1) return tuple(random.choice(pool) for pool in pools) def random_permutation(iterable, r=None): "Random selection from itertools.permutations(iterable, r)" pool = tuple(iterable) r = len(pool) if r is None else r return tuple(random.sample(pool, r)) def random_combination(iterable, r): "Random selection from itertools.combinations(iterable, r)" pool = tuple(iterable) n = len(pool) indices = sorted(random.sample(xrange(n), r)) return tuple(pool[i] for i in indices) def random_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) for i in xrange(r)) return tuple(pool[i] for i in indices) def tee_lookahead(t, i): """Inspect the i-th upcomping value from a tee object while leaving the tee object at its current position. Raise an IndexError if the underlying iterator doesn't have enough values. """ for value in islice(t.__copy__(), i, None): return value raise IndexError(i)

请注意,上述许多配方可以通过将全局查找替换为定义为默认值的局部变量进行优化。例如,dotproduct配方可以写为:

def dotproduct(vec1, vec2, sum=sum, imap=imap, mul=operator.mul): return sum(imap(mul, vec1, vec2))