From a098b33c9346ae8c8891982cd733d5d9dcdae2ec Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Mon, 8 Sep 2003 23:58:40 +0000 Subject: [PATCH] Add an example to address a common question of how to split iterators. --- Doc/lib/libitertools.tex | 27 ++++++++++++++++++++------- Lib/test/test_itertools.py | 34 ++++++++++++++++++++++++++++++---- 2 files changed, 50 insertions(+), 11 deletions(-) diff --git a/Doc/lib/libitertools.tex b/Doc/lib/libitertools.tex index 7fdd602514d..453d16e4d7b 100644 --- a/Doc/lib/libitertools.tex +++ b/Doc/lib/libitertools.tex @@ -321,13 +321,15 @@ Samuele \end{verbatim} -This section has further examples of how itertools can be combined. -Note that \function{enumerate()} and \method{iteritems()} already -have highly efficient implementations in Python. They are only -included here to illustrate how higher level tools can be created -from building blocks. +This section shows how itertools can be combined to create other more +powerful itertools. Note that \function{enumerate()} and \method{iteritems()} +already have efficient implementations in Python. They are only included here +to illustrate how higher level tools can be created from building blocks. \begin{verbatim} +def take(n, seq): + return list(islice(seq, n)) + def enumerate(iterable): return izip(count(), iterable) @@ -380,7 +382,18 @@ def window(seq, n=2): result = result[1:] + (elem,) yield result -def take(n, seq): - return list(islice(seq, n)) +def tee(iterable): + "Return two independent iterators from a single iterable" + def gen(next, data={}, cnt=[0]): + dpop = data.pop + for i in count(): + if i == cnt[0]: + item = data[i] = next() + cnt[0] += 1 + else: + item = dpop(i) + yield item + next = iter(iterable).next + return (gen(next), gen(next)) \end{verbatim} diff --git a/Lib/test/test_itertools.py b/Lib/test/test_itertools.py index 057b576e49d..8ab4cea6f00 100644 --- a/Lib/test/test_itertools.py +++ b/Lib/test/test_itertools.py @@ -487,6 +487,9 @@ Martin Walter Samuele +>>> def take(n, seq): +... return list(islice(seq, n)) + >>> def enumerate(iterable): ... return izip(count(), iterable) @@ -539,12 +542,26 @@ Samuele ... result = result[1:] + (elem,) ... yield result ->>> def take(n, seq): -... return list(islice(seq, n)) +>>> def tee(iterable): +... "Return two independent iterators from a single iterable" +... def gen(next, data={}, cnt=[0]): +... dpop = data.pop +... for i in count(): +... if i == cnt[0]: +... item = data[i] = next() +... cnt[0] += 1 +... else: +... item = dpop(i) +... yield item +... next = iter(iterable).next +... return (gen(next), gen(next)) This is not part of the examples but it tests to make sure the definitions perform as purported. +>>> take(10, count()) +[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] + >>> list(enumerate('abc')) [(0, 'a'), (1, 'b'), (2, 'c')] @@ -590,8 +607,17 @@ False >>> dotproduct([1,2,3], [4,5,6]) 32 ->>> take(10, count()) -[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] +>>> def irange(start, stop): +... for i in range(start, stop): +... yield i + +>>> x, y = tee(irange(2,10)) +>>> list(x), list(y) +([2, 3, 4, 5, 6, 7, 8, 9], [2, 3, 4, 5, 6, 7, 8, 9]) + +>>> x, y = tee(irange(2,10)) +>>> zip(x, y) +[(2, 2), (3, 3), (4, 4), (5, 5), (6, 6), (7, 7), (8, 8), (9, 9)] """