From a255190d27a71ae861346a40ae5305a3834b0878 Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Mon, 12 Jan 2026 14:12:23 -0600 Subject: [PATCH] Minor readability/usability improvement to the recipes section (gh-143753) (cherry picked from commit 66e1399311c17684c6e26f5d9d9603fbd0717d0d) Co-authored-by: Raymond Hettinger --- Doc/library/itertools.rst | 36 +++++++++++++++++++++++------------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/Doc/library/itertools.rst b/Doc/library/itertools.rst index e28d139c76e0c8..08dacb505f7748 100644 --- a/Doc/library/itertools.rst +++ b/Doc/library/itertools.rst @@ -819,7 +819,7 @@ well as with the built-in itertools such as ``map()``, ``filter()``, A secondary purpose of the recipes is to serve as an incubator. The ``accumulate()``, ``compress()``, and ``pairwise()`` itertools started out as -recipes. Currently, the ``sliding_window()``, ``iter_index()``, and ``sieve()`` +recipes. Currently, the ``sliding_window()``, ``derangements()``, and ``sieve()`` recipes are being tested to see whether they prove their worth. Substantially all of these recipes and many, many others can be installed from @@ -838,11 +838,16 @@ and :term:`generators ` which incur interpreter overhead. .. testcode:: + from itertools import (accumulate, batched, chain, combinations, compress, + count, cycle, filterfalse, groupby, islice, permutations, product, + repeat, starmap, tee, zip_longest) from collections import Counter, deque from contextlib import suppress from functools import reduce - from math import comb, prod, sumprod, isqrt - from operator import is_not, itemgetter, getitem, mul, neg + from math import comb, isqrt, prod, sumprod + from operator import getitem, is_not, itemgetter, mul, neg + + # ==== Basic one liners ==== def take(n, iterable): "Return first n items of the iterable as a list." @@ -899,8 +904,8 @@ and :term:`generators ` which incur interpreter overhead. def first_true(iterable, default=False, predicate=None): "Returns the first true value or the *default* if there is no true value." - # 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 + # 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 return next(filter(predicate, iterable), default) def all_equal(iterable, key=None): @@ -908,6 +913,8 @@ and :term:`generators ` which incur interpreter overhead. # all_equal('4٤௪౪໔', key=int) → True return len(take(2, groupby(iterable, key))) <= 1 + # ==== Data pipelines ==== + def unique_justseen(iterable, key=None): "Yield unique elements, preserving order. Remember only the element just seen." # unique_justseen('AAAABBBCCDAABBB') → A B C D A B @@ -940,7 +947,7 @@ and :term:`generators ` which incur interpreter overhead. def sliding_window(iterable, n): "Collect data into overlapping fixed-length chunks or blocks." - # sliding_window('ABCDEFG', 4) → ABCD BCDE CDEF DEFG + # sliding_window('ABCDEFG', 3) → ABC BCD CDE DEF EFG iterator = iter(iterable) window = deque(islice(iterator, n - 1), maxlen=n) for x in iterator: @@ -949,7 +956,7 @@ and :term:`generators ` which incur interpreter overhead. def grouper(iterable, n, *, incomplete='fill', fillvalue=None): "Collect data into non-overlapping fixed-length chunks or blocks." - # grouper('ABCDEFG', 3, fillvalue='x') → ABC DEF Gxx + # grouper('ABCDEFG', 3, fillvalue='x') → ABC DEF Gxx # grouper('ABCDEFG', 3, incomplete='strict') → ABC DEF ValueError # grouper('ABCDEFG', 3, incomplete='ignore') → ABC DEF iterators = [iter(iterable)] * n @@ -1014,10 +1021,7 @@ and :term:`generators ` which incur interpreter overhead. while True: yield function() - -The following recipes have a more mathematical flavor: - -.. testcode:: + # ==== Mathematical operations ==== def multinomial(*counts): "Number of distinct arrangements of a multiset." @@ -1036,9 +1040,11 @@ The following recipes have a more mathematical flavor: # sum_of_squares([10, 20, 30]) → 1400 return sumprod(*tee(iterable)) + # ==== Matrix operations ==== + def reshape(matrix, columns): "Reshape a 2-D matrix to have a given number of columns." - # reshape([(0, 1), (2, 3), (4, 5)], 3) → (0, 1, 2), (3, 4, 5) + # reshape([(0, 1), (2, 3), (4, 5)], 3) → (0, 1, 2) (3, 4, 5) return batched(chain.from_iterable(matrix), columns, strict=True) def transpose(matrix): @@ -1048,10 +1054,12 @@ The following recipes have a more mathematical flavor: def matmul(m1, m2): "Multiply two matrices." - # matmul([(7, 5), (3, 5)], [(2, 5), (7, 9)]) → (49, 80), (41, 60) + # matmul([(7, 5), (3, 5)], [(2, 5), (7, 9)]) → (49, 80) (41, 60) n = len(m2[0]) return batched(starmap(sumprod, product(m1, transpose(m2))), n) + # ==== Polynomial arithmetic ==== + def convolve(signal, kernel): """Discrete linear convolution of two iterables. Equivalent to polynomial multiplication. @@ -1106,6 +1114,8 @@ The following recipes have a more mathematical flavor: powers = reversed(range(1, n)) return list(map(mul, coefficients, powers)) + # ==== Number theory ==== + def sieve(n): "Primes less than n." # sieve(30) → 2 3 5 7 11 13 17 19 23 29