1010.. testsetup ::
1111
1212 from itertools import *
13+ import collections
14+ import math
15+ import operator
16+ import random
1317
1418--------------
1519
@@ -132,10 +136,9 @@ loops that truncate the stream.
132136 There are a number of uses for the *func * argument. It can be set to
133137 :func: `min ` for a running minimum, :func: `max ` for a running maximum, or
134138 :func: `operator.mul ` for a running product. Amortization tables can be
135- built by accumulating interest and applying payments. First-order
136- `recurrence relations <https://en.wikipedia.org/wiki/Recurrence_relation >`_
137- can be modeled by supplying the initial value in the iterable and using only
138- the accumulated total in *func * argument::
139+ built by accumulating interest and applying payments:
140+
141+ .. doctest ::
139142
140143 >>> data = [3 , 4 , 6 , 2 , 1 , 9 , 0 , 7 , 5 , 8 ]
141144 >>> list (accumulate(data, operator.mul)) # running product
@@ -148,17 +151,6 @@ loops that truncate the stream.
148151 >>> list (accumulate(cashflows, lambda bal , pmt : bal* 1.05 + pmt))
149152 [1000, 960.0, 918.0, 873.9000000000001, 827.5950000000001]
150153
151- # Chaotic recurrence relation https://en.wikipedia.org/wiki/Logistic_map
152- >>> logistic_map = lambda x, _: r * x * (1 - x)
153- >>> r = 3.8
154- >>> x0 = 0.4
155- >>> inputs = repeat(x0, 36) # only the initial value is used
156- >>> [format(x, '.2f') for x in accumulate(inputs, logistic_map)]
157- ['0.40', '0.91', '0.30', '0.81', '0.60', '0.92', '0.29', '0.79', '0.63',
158- '0.88', '0.39', '0.90', '0.33', '0.84', '0.52', '0.95', '0.18', '0.57',
159- '0.93', '0.25', '0.71', '0.79', '0.63', '0.88', '0.39', '0.91', '0.32',
160- '0.83', '0.54', '0.95', '0.20', '0.60', '0.91', '0.30', '0.80', '0.60']
161-
162154 See :func: `functools.reduce ` for a similar function that returns only the
163155 final accumulated value.
164156
@@ -202,10 +194,10 @@ loops that truncate the stream.
202194
203195 The combination tuples are emitted in lexicographic ordering according to
204196 the order of the input *iterable *. So, if the input *iterable * is sorted,
205- the combination tuples will be produced in sorted order.
197+ the output tuples will be produced in sorted order.
206198
207199 Elements are treated as unique based on their position, not on their
208- value. So if the input elements are unique, there will be no repeat
200+ value. So if the input elements are unique, there will be no repeated
209201 values in each combination.
210202
211203 Roughly equivalent to::
@@ -251,7 +243,7 @@ loops that truncate the stream.
251243
252244 The combination tuples are emitted in lexicographic ordering according to
253245 the order of the input *iterable *. So, if the input *iterable * is sorted,
254- the combination tuples will be produced in sorted order.
246+ the output tuples will be produced in sorted order.
255247
256248 Elements are treated as unique based on their position, not on their
257249 value. So if the input elements are unique, the generated combinations
@@ -410,21 +402,25 @@ loops that truncate the stream.
410402 class groupby:
411403 # [k for k, g in groupby('AAAABBBCCDAABBB')] --> A B C D A B
412404 # [list(g) for k, g in groupby('AAAABBBCCD')] --> AAAA BBB CC D
405+
413406 def __init__(self, iterable, key=None):
414407 if key is None:
415408 key = lambda x: x
416409 self.keyfunc = key
417410 self.it = iter(iterable)
418411 self.tgtkey = self.currkey = self.currvalue = object()
412+
419413 def __iter__(self):
420414 return self
415+
421416 def __next__(self):
422417 self.id = object()
423418 while self.currkey == self.tgtkey:
424419 self.currvalue = next(self.it) # Exit on StopIteration
425420 self.currkey = self.keyfunc(self.currvalue)
426421 self.tgtkey = self.currkey
427422 return (self.currkey, self._grouper(self.tgtkey, self.id))
423+
428424 def _grouper(self, tgtkey, id):
429425 while self.id is id and self.currkey == tgtkey:
430426 yield self.currvalue
@@ -443,10 +439,17 @@ loops that truncate the stream.
443439 Afterward, elements are returned consecutively unless *step * is set higher than
444440 one which results in items being skipped. If *stop * is ``None ``, then iteration
445441 continues until the iterator is exhausted, if at all; otherwise, it stops at the
446- specified position. Unlike regular slicing, :func: `islice ` does not support
447- negative values for *start *, *stop *, or *step *. Can be used to extract related
448- fields from data where the internal structure has been flattened (for example, a
449- multi-line report may list a name field on every third line). Roughly equivalent to::
442+ specified position.
443+
444+ If *start * is ``None ``, then iteration starts at zero. If *step * is ``None ``,
445+ then the step defaults to one.
446+
447+ Unlike regular slicing, :func: `islice ` does not support negative values for
448+ *start *, *stop *, or *step *. Can be used to extract related fields from
449+ data where the internal structure has been flattened (for example, a
450+ multi-line report may list a name field on every third line).
451+
452+ Roughly equivalent to::
450453
451454 def islice(iterable, *args):
452455 # islice('ABCDEFG', 2) --> A B
@@ -473,8 +476,6 @@ loops that truncate the stream.
473476 for i, element in zip(range(i + 1, stop), iterable):
474477 pass
475478
476- If *start * is ``None ``, then iteration starts at zero. If *step * is ``None ``,
477- then the step defaults to one.
478479
479480.. function :: pairwise(iterable)
480481
@@ -503,13 +504,13 @@ loops that truncate the stream.
503504 of the *iterable * and all possible full-length permutations
504505 are generated.
505506
506- The permutation tuples are emitted in lexicographic ordering according to
507+ The permutation tuples are emitted in lexicographic order according to
507508 the order of the input *iterable *. So, if the input *iterable * is sorted,
508- the combination tuples will be produced in sorted order.
509+ the output tuples will be produced in sorted order.
509510
510511 Elements are treated as unique based on their position, not on their
511- value. So if the input elements are unique, there will be no repeat
512- values in each permutation.
512+ value. So if the input elements are unique, there will be no repeated
513+ values within a permutation.
513514
514515 Roughly equivalent to::
515516
@@ -589,9 +590,7 @@ loops that truncate the stream.
589590.. function :: repeat(object[, times])
590591
591592 Make an iterator that returns *object * over and over again. Runs indefinitely
592- unless the *times * argument is specified. Used as argument to :func: `map ` for
593- invariant parameters to the called function. Also used with :func: `zip ` to
594- create an invariant part of a tuple record.
593+ unless the *times * argument is specified.
595594
596595 Roughly equivalent to::
597596
@@ -605,7 +604,9 @@ loops that truncate the stream.
605604 yield object
606605
607606 A common use for *repeat * is to supply a stream of constant values to *map *
608- or *zip *::
607+ or *zip *:
608+
609+ .. doctest ::
609610
610611 >>> list (map (pow , range (10 ), repeat(2 )))
611612 [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
@@ -614,9 +615,12 @@ loops that truncate the stream.
614615
615616 Make an iterator that computes the function using arguments obtained from
616617 the iterable. Used instead of :func: `map ` when argument parameters are already
617- grouped in tuples from a single iterable (the data has been "pre-zipped"). The
618- difference between :func: `map ` and :func: `starmap ` parallels the distinction
619- between ``function(a,b) `` and ``function(*c) ``. Roughly equivalent to::
618+ grouped in tuples from a single iterable (when the data has been
619+ "pre-zipped").
620+
621+ The difference between :func: `map ` and :func: `starmap ` parallels the
622+ distinction between ``function(a,b) `` and ``function(*c) ``. Roughly
623+ equivalent to::
620624
621625 def starmap(function, iterable):
622626 # starmap(pow, [(2,5), (3,2), (10,3)]) --> 32 9 1000
@@ -644,9 +648,7 @@ loops that truncate the stream.
644648
645649 The following Python code helps explain what *tee * does (although the actual
646650 implementation is more complex and uses only a single underlying
647- :abbr: `FIFO ( first-in, first-out ) ` queue).
648-
649- Roughly equivalent to::
651+ :abbr: `FIFO ( first-in, first-out ) ` queue)::
650652
651653 def tee(iterable, n=2):
652654 it = iter(iterable)
@@ -663,7 +665,7 @@ loops that truncate the stream.
663665 yield mydeque.popleft()
664666 return tuple(gen(d) for d in deques)
665667
666- Once :func: `tee ` has made a split , the original *iterable * should not be
668+ Once a :func: `tee ` has been created , the original *iterable * should not be
667669 used anywhere else; otherwise, the *iterable * could get advanced without
668670 the tee objects being informed.
669671
@@ -717,14 +719,28 @@ Itertools Recipes
717719This section shows recipes for creating an extended toolset using the existing
718720itertools as building blocks.
719721
722+ The primary purpose of the itertools recipes is educational. The recipes show
723+ various ways of thinking about individual tools — for example, that
724+ ``chain.from_iterable `` is related to the concept of flattening. The recipes
725+ also give ideas about ways that the tools can be combined — for example, how
726+ `compress() ` and `range() ` can work together. The recipes also show patterns
727+ for using itertools with the :mod: `operator ` and :mod: `collections ` modules as
728+ well as with the built-in itertools such as ``map() ``, ``filter() ``,
729+ ``reversed() ``, and ``enumerate() ``.
730+
731+ A secondary purpose of the recipes is to serve as an incubator. The
732+ ``accumulate() ``, ``compress() ``, and ``pairwise() `` itertools started out as
733+ recipes. Currently, the ``iter_index() `` recipe is being tested to see
734+ whether it proves its worth.
735+
720736Substantially all of these recipes and many, many others can be installed from
721737the `more-itertools project <https://pypi.org/project/more-itertools/ >`_ found
722738on the Python Package Index::
723739
724740 python -m pip install more-itertools
725741
726- The extended tools offer the same high performance as the underlying toolset.
727- The superior memory performance is kept by processing elements one at a time
742+ Many of the recipes offer the same high performance as the underlying toolset.
743+ Superior memory performance is kept by processing elements one at a time
728744rather than bringing the whole iterable into memory all at once. Code volume is
729745kept small by linking the tools together in a functional style which helps
730746eliminate temporary variables. High speed is retained by preferring
@@ -809,15 +825,25 @@ which incur interpreter overhead.
809825 for k in range(len(roots) + 1)
810826 ]
811827
812- def iter_index(seq , value, start=0):
813- "Return indices where a value occurs in a sequence."
828+ def iter_index(iterable , value, start=0):
829+ "Return indices where a value occurs in a sequence or iterable ."
814830 # iter_index('AABCADEAF', 'A') --> 0 1 4 7
815- i = start - 1
816831 try:
817- while True:
818- yield (i := seq.index(value, i+1))
819- except ValueError:
820- pass
832+ seq_index = iterable.index
833+ except AttributeError:
834+ # Slow path for general iterables
835+ it = islice(iterable, start, None)
836+ for i, element in enumerate(it, start):
837+ if element is value or element == value:
838+ yield i
839+ else:
840+ # Fast path for sequences
841+ i = start - 1
842+ try:
843+ while True:
844+ yield (i := seq_index(value, i+1))
845+ except ValueError:
846+ pass
821847
822848 def sieve(n):
823849 "Primes less than n"
@@ -946,16 +972,19 @@ which incur interpreter overhead.
946972 # unique_everseen('AAAABBBCCDAABBB') --> A B C D
947973 # unique_everseen('ABBCcAD', str.lower) --> A B C D
948974 seen = set()
949- seen_add = seen.add
950975 if key is None:
951976 for element in filterfalse(seen.__contains__, iterable):
952- seen_add (element)
977+ seen.add (element)
953978 yield element
979+ # Note: The steps shown above are intended to demonstrate
980+ # filterfalse(). For order preserving deduplication,
981+ # a better solution is:
982+ # yield from dict.fromkeys(iterable)
954983 else:
955984 for element in iterable:
956985 k = key(element)
957986 if k not in seen:
958- seen_add (k)
987+ seen.add (k)
959988 yield element
960989
961990 def unique_justseen(iterable, key=None):
@@ -1164,6 +1193,18 @@ which incur interpreter overhead.
11641193 []
11651194 >>> list (iter_index(' ' , ' X' ))
11661195 []
1196+ >>> list (iter_index(' AABCADEAF' , ' A' , 1 ))
1197+ [1, 4, 7]
1198+ >>> list (iter_index(iter (' AABCADEAF' ), ' A' , 1 ))
1199+ [1, 4, 7]
1200+ >>> list (iter_index(' AABCADEAF' , ' A' , 2 ))
1201+ [4, 7]
1202+ >>> list (iter_index(iter (' AABCADEAF' ), ' A' , 2 ))
1203+ [4, 7]
1204+ >>> list (iter_index(' AABCADEAF' , ' A' , 10 ))
1205+ []
1206+ >>> list (iter_index(iter (' AABCADEAF' ), ' A' , 10 ))
1207+ []
11671208
11681209 >>> list (sieve(30 ))
11691210 [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]
0 commit comments