]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
Misc improvements to the itertools docs (gh-125147)
authorRaymond Hettinger <rhettinger@users.noreply.github.com>
Tue, 8 Oct 2024 19:02:58 +0000 (14:02 -0500)
committerGitHub <noreply@github.com>
Tue, 8 Oct 2024 19:02:58 +0000 (14:02 -0500)
Doc/library/itertools.rst

index 9a62249816c9bf24540ba1f36287384ac872b543..c138e903fa5a0f98e7bde301b67749b142063228 100644 (file)
@@ -134,7 +134,7 @@ loops that truncate the stream.
     To compute a running minimum, set *function* to :func:`min`.
     For a running maximum, set *function* to :func:`max`.
     Or for a running product, set *function* to :func:`operator.mul`.
-    To build an `Amortization table
+    To build an `amortization table
     <https://www.ramseysolutions.com/real-estate/amortization-schedule>`_,
     accumulate the interest and apply payments:
 
@@ -736,6 +736,26 @@ loops that truncate the stream.
    allows nested :func:`tee` calls to share the same underlying data
    chain and to have a single update step rather than a chain of calls.
 
+   The flattening property makes tee iterators efficiently peekable:
+
+   .. testcode::
+
+      def lookahead(tee_iterator):
+           "Return the next value without moving the input forward"
+           [forked_iterator] = tee(tee_iterator, 1)
+           return next(forked_iterator)
+
+   .. doctest::
+
+      >>> iterator = iter('abcdef')
+      >>> [iterator] = tee(iterator, 1)   # Make the input peekable
+      >>> next(iterator)                  # Move the iterator forward
+      'a'
+      >>> lookahead(iterator)             # Check next value
+      'b'
+      >>> next(iterator)                  # Continue moving forward
+      'b'
+
    ``tee`` iterators are not threadsafe. A :exc:`RuntimeError` may be
    raised when simultaneously using iterators returned by the same :func:`tee`
    call, even if the original *iterable* is threadsafe.
@@ -952,15 +972,6 @@ and :term:`generators <generator>` which incur interpreter overhead.
            iterators = cycle(islice(iterators, num_active))
            yield from map(next, iterators)
 
-   def partition(predicate, iterable):
-       """Partition entries into false entries and true entries.
-
-       If *predicate* is slow, consider wrapping it with functools.lru_cache().
-       """
-       # partition(is_odd, range(10)) → 0 2 4 6 8   and  1 3 5 7 9
-       t1, t2 = tee(iterable)
-       return filterfalse(predicate, t1), filter(predicate, t2)
-
    def subslices(seq):
        "Return all contiguous non-empty subslices of a sequence."
        # subslices('ABCD') → A AB ABC ABCD B BC BCD C CD D
@@ -1178,15 +1189,19 @@ The following recipes have a more mathematical flavor:
     >>> list(it)
     ['d', 'e', 'f']
 
+
     >>> list(prepend(1, [2, 3, 4]))
     [1, 2, 3, 4]
 
+
     >>> list(enumerate('abc'))
     [(0, 'a'), (1, 'b'), (2, 'c')]
 
+
     >>> list(islice(tabulate(lambda x: 2*x), 4))
     [0, 2, 4, 6]
 
+
     >>> list(tail(3, 'ABCDEFG'))
     ['E', 'F', 'G']
     >>> # Verify the input is consumed greedily
@@ -1195,6 +1210,7 @@ The following recipes have a more mathematical flavor:
     >>> list(input_iterator)
     []
 
+
     >>> it = iter(range(10))
     >>> consume(it, 3)
     >>> # Verify the input is consumed lazily
@@ -1205,6 +1221,7 @@ The following recipes have a more mathematical flavor:
     >>> next(it, 'Done')
     'Done'
 
+
     >>> nth('abcde', 3)
     'd'
     >>> nth('abcde', 9) is None
@@ -1216,6 +1233,7 @@ The following recipes have a more mathematical flavor:
     >>> list(it)
     ['d', 'e']
 
+
     >>> [all_equal(s) for s in ('', 'A', 'AAAA', 'AAAB', 'AAABA')]
     [True, True, True, False, False]
     >>> [all_equal(s, key=str.casefold) for s in ('', 'A', 'AaAa', 'AAAB', 'AAABA')]
@@ -1229,24 +1247,19 @@ The following recipes have a more mathematical flavor:
     >>> ''.join(it)
     'bbccc'
 
+
     >>> quantify(range(99), lambda x: x%2==0)
     50
-
     >>> quantify([True, False, False, True, True])
     3
-
     >>> quantify(range(12), predicate=lambda x: x%2==1)
     6
 
+
     >>> a = [[1, 2, 3], [4, 5, 6]]
     >>> list(flatten(a))
     [1, 2, 3, 4, 5, 6]
 
-    >>> list(repeatfunc(pow, 5, 2, 3))
-    [8, 8, 8, 8, 8]
-
-    >>> take(5, map(int, repeatfunc(random.random)))
-    [0, 0, 0, 0, 0]
 
     >>> list(ncycles('abc', 3))
     ['a', 'b', 'c', 'a', 'b', 'c', 'a', 'b', 'c']
@@ -1256,9 +1269,11 @@ The following recipes have a more mathematical flavor:
     >>> list(input_iterator)
     []
 
+
     >>> sum_of_squares([10, 20, 30])
     1400
 
+
     >>> list(reshape([(0, 1), (2, 3), (4, 5)], 3))
     [(0, 1, 2), (3, 4, 5)]
     >>> M = [(0, 1, 2, 3), (4, 5, 6, 7), (8, 9, 10, 11)]
@@ -1279,6 +1294,7 @@ The following recipes have a more mathematical flavor:
     >>> list(reshape(M, 12))
     [(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11)]
 
+
     >>> list(transpose([(1, 2, 3), (11, 22, 33)]))
     [(1, 11), (2, 22), (3, 33)]
     >>> # Verify that the inputs are consumed lazily
@@ -1290,11 +1306,13 @@ The following recipes have a more mathematical flavor:
     >>> list(zip(input1, input2))
     [(2, 22), (3, 33)]
 
+
     >>> list(matmul([(7, 5), (3, 5)], [[2, 5], [7, 9]]))
     [(49, 80), (41, 60)]
     >>> list(matmul([[2, 5], [7, 9], [3, 4]], [[7, 11, 5, 4, 9], [3, 5, 2, 6, 3]]))
     [(29, 47, 20, 38, 33), (76, 122, 53, 82, 90), (33, 53, 23, 36, 39)]
 
+
     >>> list(convolve([1, -1, -20], [1, -3])) == [1, -4, -17, 60]
     True
     >>> data = [20, 40, 24, 32, 20, 28, 16]
@@ -1317,6 +1335,7 @@ The following recipes have a more mathematical flavor:
     >>> list(signal_iterator)
     [30, 40, 50]
 
+
     >>> from fractions import Fraction
     >>> from decimal import Decimal
     >>> polynomial_eval([1, -4, -17, 60], x=5)
@@ -1348,6 +1367,7 @@ The following recipes have a more mathematical flavor:
     >>> polynomial_eval([11, 2], 7) == 11 * 7 + 2
     True
 
+
     >>> polynomial_from_roots([5, -4, 3])
     [1, -4, -17, 60]
     >>> factored = lambda x: (x - 5) * (x + 4) * (x - 3)
@@ -1355,9 +1375,11 @@ The following recipes have a more mathematical flavor:
     >>> all(factored(x) == expanded(x) for x in range(-10, 11))
     True
 
+
     >>> polynomial_derivative([1, -4, -17, 60])
     [3, -8, -17]
 
+
     >>> list(iter_index('AABCADEAF', 'A'))
     [0, 1, 4, 7]
     >>> list(iter_index('AABCADEAF', 'B'))
@@ -1415,12 +1437,14 @@ The following recipes have a more mathematical flavor:
     >>> ''.join(input_iterator)
     'DEAF'
 
+
     >>> # Verify that the target value can be a sequence.
     >>> seq = [[10, 20], [30, 40], 30, 40, [30, 40], 50]
     >>> target = [30, 40]
     >>> list(iter_index(seq, target))
     [1, 4]
 
+
     >>> # Verify faithfulness to type specific index() method behaviors.
     >>> # For example, bytes and str perform continuous-subsequence searches
     >>> # that do not match the general behavior specified
@@ -1450,6 +1474,7 @@ The following recipes have a more mathematical flavor:
     >>> set(sieve(10_000)).isdisjoint(carmichael)
     True
 
+
     >>> list(factor(99))                    # Code example 1
     [3, 3, 11]
     >>> list(factor(1_000_000_000_000_007)) # Code example 2
@@ -1495,6 +1520,7 @@ The following recipes have a more mathematical flavor:
     >>> all(list(factor(n)) == sorted(factor(n)) for n in range(2_000))
     True
 
+
     >>> totient(0)  # https://www.wolframalpha.com/input?i=totient+0
     0
     >>> first_totients = [1, 1, 2, 2, 4, 2, 6, 4, 6, 4, 10, 4, 12, 6, 8, 8, 16, 6,
@@ -1514,9 +1540,15 @@ The following recipes have a more mathematical flavor:
     >>> totient(6 ** 20) == 1 * 2**19 * 2 * 3**19    # repeated primes
     True
 
+
     >>> list(flatten([('a', 'b'), (), ('c', 'd', 'e'), ('f',), ('g', 'h', 'i')]))
     ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i']
 
+
+    >>> list(repeatfunc(pow, 5, 2, 3))
+    [8, 8, 8, 8, 8]
+    >>> take(5, map(int, repeatfunc(random.random)))
+    [0, 0, 0, 0, 0]
     >>> random.seed(85753098575309)
     >>> list(repeatfunc(random.random, 3))
     [0.16370491282496968, 0.45889608687313455, 0.3747076837820118]
@@ -1525,9 +1557,11 @@ The following recipes have a more mathematical flavor:
     >>> list(repeatfunc(pow, 3, 2, 5))
     [32, 32, 32]
 
+
     >>> list(grouper('abcdefg', 3, fillvalue='x'))
     [('a', 'b', 'c'), ('d', 'e', 'f'), ('g', 'x', 'x')]
 
+
     >>> it = grouper('abcdefg', 3, incomplete='strict')
     >>> next(it)
     ('a', 'b', 'c')
@@ -1541,6 +1575,7 @@ The following recipes have a more mathematical flavor:
     >>> list(grouper('abcdefg', n=3, incomplete='ignore'))
     [('a', 'b', 'c'), ('d', 'e', 'f')]
 
+
     >>> list(sliding_window('ABCDEFG', 1))
     [('A',), ('B',), ('C',), ('D',), ('E',), ('F',), ('G',)]
     >>> list(sliding_window('ABCDEFG', 2))
@@ -1570,6 +1605,7 @@ The following recipes have a more mathematical flavor:
     ...
     'zero or negative n not supported'
 
+
     >>> list(roundrobin('abc', 'd', 'ef'))
     ['a', 'd', 'e', 'b', 'f', 'c']
     >>> ranges = [range(5, 1000), range(4, 3000), range(0), range(3, 2000), range(2, 5000), range(1, 3500)]
@@ -1583,38 +1619,19 @@ The following recipes have a more mathematical flavor:
     >>> ''.join(chain(*input_iterators))
     'dijkopqr'
 
-    >>> def is_odd(x):
-    ...     return x % 2 == 1
-
-    >>> evens, odds = partition(is_odd, range(10))
-    >>> list(evens)
-    [0, 2, 4, 6, 8]
-    >>> list(odds)
-    [1, 3, 5, 7, 9]
-    >>> # Verify that the input is consumed lazily
-    >>> input_iterator = iter(range(10))
-    >>> evens, odds = partition(is_odd, input_iterator)
-    >>> next(odds)
-    1
-    >>> next(odds)
-    3
-    >>> next(evens)
-    0
-    >>> list(input_iterator)
-    [4, 5, 6, 7, 8, 9]
 
     >>> list(subslices('ABCD'))
     ['A', 'AB', 'ABC', 'ABCD', 'B', 'BC', 'BCD', 'C', 'CD', 'D']
 
+
     >>> list(powerset([1,2,3]))
     [(), (1,), (2,), (3,), (1, 2), (1, 3), (2, 3), (1, 2, 3)]
-
     >>> all(len(list(powerset(range(n)))) == 2**n for n in range(18))
     True
-
     >>> list(powerset('abcde')) == sorted(sorted(set(powerset('abcde'))), key=len)
     True
 
+
     >>> list(unique_everseen('AAAABBBCCDAABBB'))
     ['A', 'B', 'C', 'D']
     >>> list(unique_everseen('ABBCcAD', str.casefold))
@@ -1629,6 +1646,7 @@ The following recipes have a more mathematical flavor:
     >>> ''.join(input_iterator)
     'AAABBBCCDAABBB'
 
+
     >>> list(unique_justseen('AAAABBBCCDAABBB'))
     ['A', 'B', 'C', 'D', 'A', 'B']
     >>> list(unique_justseen('ABBCcAD', str.casefold))
@@ -1643,6 +1661,7 @@ The following recipes have a more mathematical flavor:
     >>> ''.join(input_iterator)
     'AAABBBCCDAABBB'
 
+
     >>> list(unique([[1, 2], [3, 4], [1, 2]]))
     [[1, 2], [3, 4]]
     >>> list(unique('ABBcCAD', str.casefold))
@@ -1650,6 +1669,7 @@ The following recipes have a more mathematical flavor:
     >>> list(unique('ABBcCAD', str.casefold, reverse=True))
     ['D', 'c', 'B', 'A']
 
+
     >>> d = dict(a=1, b=2, c=3)
     >>> it = iter_except(d.popitem, KeyError)
     >>> d['d'] = 4
@@ -1667,6 +1687,7 @@ The following recipes have a more mathematical flavor:
     >>> next(it, 'empty')
     'empty'
 
+
     >>> first_true('ABC0DEF1', '9', str.isdigit)
     '0'
     >>> # Verify that inputs are consumed lazily
@@ -1747,21 +1768,36 @@ The following recipes have a more mathematical flavor:
 
        return true_iterator(), chain(transition, it)
 
+    def partition(predicate, iterable):
+        """Partition entries into false entries and true entries.
+
+        If *predicate* is slow, consider wrapping it with functools.lru_cache().
+        """
+        # partition(is_odd, range(10)) → 0 2 4 6 8   and  1 3 5 7 9
+        t1, t2 = tee(iterable)
+        return filterfalse(predicate, t1), filter(predicate, t2)
+
+
+
 .. doctest::
     :hide:
 
     >>> dotproduct([1,2,3], [4,5,6])
     32
 
+
     >>> sumprod([1,2,3], [4,5,6])
     32
 
+
     >>> list(islice(pad_none('abc'), 0, 6))
     ['a', 'b', 'c', None, None, None]
 
+
     >>> list(triplewise('ABCDEFG'))
     [('A', 'B', 'C'), ('B', 'C', 'D'), ('C', 'D', 'E'), ('D', 'E', 'F'), ('E', 'F', 'G')]
 
+
     >>> population = 'ABCDEFGH'
     >>> for r in range(len(population) + 1):
     ...     seq = list(combinations(population, r))
@@ -1769,16 +1805,38 @@ The following recipes have a more mathematical flavor:
     ...         assert nth_combination(population, r, i) == seq[i]
     ...     for i in range(-len(seq), 0):
     ...         assert nth_combination(population, r, i) == seq[i]
-
+    ...
     >>> iterable = 'abcde'
     >>> r = 3
     >>> combos = list(combinations(iterable, r))
     >>> all(nth_combination(iterable, r, i) == comb for i, comb in enumerate(combos))
     True
 
+
     >>> it = iter('ABCdEfGhI')
     >>> all_upper, remainder = before_and_after(str.isupper, it)
     >>> ''.join(all_upper)
     'ABC'
     >>> ''.join(remainder)
     'dEfGhI'
+
+
+    >>> def is_odd(x):
+    ...     return x % 2 == 1
+    ...
+    >>> evens, odds = partition(is_odd, range(10))
+    >>> list(evens)
+    [0, 2, 4, 6, 8]
+    >>> list(odds)
+    [1, 3, 5, 7, 9]
+    >>> # Verify that the input is consumed lazily
+    >>> input_iterator = iter(range(10))
+    >>> evens, odds = partition(is_odd, input_iterator)
+    >>> next(odds)
+    1
+    >>> next(odds)
+    3
+    >>> next(evens)
+    0
+    >>> list(input_iterator)
+    [4, 5, 6, 7, 8, 9]