summaryrefslogtreecommitdiffstats
path: root/Doc/library
diff options
context:
space:
mode:
authorRaymond Hettinger <rhettinger@users.noreply.github.com>2024-10-08 19:02:58 (GMT)
committerGitHub <noreply@github.com>2024-10-08 19:02:58 (GMT)
commitb2a7272408593355c4c8e1d2ce9018cf96691bea (patch)
treefbd8cb8359e25b9e924efff07b6294270a519fb8 /Doc/library
parent760b1e103a0aa696cdf448e0d500cd1bac2213fa (diff)
downloadcpython-b2a7272408593355c4c8e1d2ce9018cf96691bea.zip
cpython-b2a7272408593355c4c8e1d2ce9018cf96691bea.tar.gz
cpython-b2a7272408593355c4c8e1d2ce9018cf96691bea.tar.bz2
Misc improvements to the itertools docs (gh-125147)
Diffstat (limited to 'Doc/library')
-rw-r--r--Doc/library/itertools.rst136
1 files changed, 97 insertions, 39 deletions
diff --git a/Doc/library/itertools.rst b/Doc/library/itertools.rst
index 9a62249..c138e90 100644
--- a/Doc/library/itertools.rst
+++ b/Doc/library/itertools.rst
@@ -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]