Misc improvements to the itertools docs (gh-125147)

This commit is contained in:
Raymond Hettinger 2024-10-08 14:02:58 -05:00 committed by GitHub
parent 760b1e103a
commit b2a7272408
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1 changed files with 97 additions and 39 deletions

View 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]