Skip to content

Commit 292067f

Browse files
authored
Minor readability improvements for the itertools recipes (gh-127928)
1 parent 5dd775b commit 292067f

File tree

1 file changed

+35
-39
lines changed

1 file changed

+35
-39
lines changed

Doc/library/itertools.rst

Lines changed: 35 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,6 @@ For instance, SML provides a tabulation tool: ``tabulate(f)`` which produces a
3030
sequence ``f(0), f(1), ...``. The same effect can be achieved in Python
3131
by combining :func:`map` and :func:`count` to form ``map(f, count())``.
3232

33-
These tools and their built-in counterparts also work well with the high-speed
34-
functions in the :mod:`operator` module. For example, the multiplication
35-
operator can be mapped across two vectors to form an efficient dot-product:
36-
``sum(starmap(operator.mul, zip(vec1, vec2, strict=True)))``.
37-
3833

3934
**Infinite iterators:**
4035

@@ -843,12 +838,11 @@ and :term:`generators <generator>` which incur interpreter overhead.
843838

844839
.. testcode::
845840

846-
import collections
847-
import contextlib
848-
import functools
849-
import math
850-
import operator
851-
import random
841+
from collections import deque
842+
from contextlib import suppress
843+
from functools import reduce
844+
from math import sumprod, isqrt
845+
from operator import itemgetter, getitem, mul, neg
852846

853847
def take(n, iterable):
854848
"Return first n items of the iterable as a list."
@@ -863,11 +857,11 @@ and :term:`generators <generator>` which incur interpreter overhead.
863857
"Return function(0), function(1), ..."
864858
return map(function, count(start))
865859

866-
def repeatfunc(func, times=None, *args):
867-
"Repeat calls to func with specified arguments."
860+
def repeatfunc(function, times=None, *args):
861+
"Repeat calls to a function with specified arguments."
868862
if times is None:
869-
return starmap(func, repeat(args))
870-
return starmap(func, repeat(args, times))
863+
return starmap(function, repeat(args))
864+
return starmap(function, repeat(args, times))
871865
872866
def flatten(list_of_lists):
873867
"Flatten one level of nesting."
@@ -885,13 +879,13 @@ and :term:`generators <generator>` which incur interpreter overhead.
885879
def tail(n, iterable):
886880
"Return an iterator over the last n items."
887881
# tail(3, 'ABCDEFG') → E F G
888-
return iter(collections.deque(iterable, maxlen=n))
882+
return iter(deque(iterable, maxlen=n))
889883

890884
def consume(iterator, n=None):
891885
"Advance the iterator n-steps ahead. If n is None, consume entirely."
892886
# Use functions that consume iterators at C speed.
893887
if n is None:
894-
collections.deque(iterator, maxlen=0)
888+
deque(iterator, maxlen=0)
895889
else:
896890
next(islice(iterator, n, n), None)
897891

@@ -919,8 +913,8 @@ and :term:`generators <generator>` which incur interpreter overhead.
919913
# unique_justseen('AAAABBBCCDAABBB') → A B C D A B
920914
# unique_justseen('ABBcCAD', str.casefold) → A B c A D
921915
if key is None:
922-
return map(operator.itemgetter(0), groupby(iterable))
923-
return map(next, map(operator.itemgetter(1), groupby(iterable, key)))
916+
return map(itemgetter(0), groupby(iterable))
917+
return map(next, map(itemgetter(1), groupby(iterable, key)))
924918

925919
def unique_everseen(iterable, key=None):
926920
"Yield unique elements, preserving order. Remember all elements ever seen."
@@ -941,13 +935,14 @@ and :term:`generators <generator>` which incur interpreter overhead.
941935
def unique(iterable, key=None, reverse=False):
942936
"Yield unique elements in sorted order. Supports unhashable inputs."
943937
# unique([[1, 2], [3, 4], [1, 2]]) → [1, 2] [3, 4]
944-
return unique_justseen(sorted(iterable, key=key, reverse=reverse), key=key)
938+
sequenced = sorted(iterable, key=key, reverse=reverse)
939+
return unique_justseen(sequenced, key=key)
945940

946941
def sliding_window(iterable, n):
947942
"Collect data into overlapping fixed-length chunks or blocks."
948943
# sliding_window('ABCDEFG', 4) → ABCD BCDE CDEF DEFG
949944
iterator = iter(iterable)
950-
window = collections.deque(islice(iterator, n - 1), maxlen=n)
945+
window = deque(islice(iterator, n - 1), maxlen=n)
951946
for x in iterator:
952947
window.append(x)
953948
yield tuple(window)
@@ -981,7 +976,7 @@ and :term:`generators <generator>` which incur interpreter overhead.
981976
"Return all contiguous non-empty subslices of a sequence."
982977
# subslices('ABCD') → A AB ABC ABCD B BC BCD C CD D
983978
slices = starmap(slice, combinations(range(len(seq) + 1), 2))
984-
return map(operator.getitem, repeat(seq), slices)
979+
return map(getitem, repeat(seq), slices)
985980

986981
def iter_index(iterable, value, start=0, stop=None):
987982
"Return indices where a value occurs in a sequence or iterable."
@@ -995,39 +990,40 @@ and :term:`generators <generator>` which incur interpreter overhead.
995990
else:
996991
stop = len(iterable) if stop is None else stop
997992
i = start
998-
with contextlib.suppress(ValueError):
993+
with suppress(ValueError):
999994
while True:
1000995
yield (i := seq_index(value, i, stop))
1001996
i += 1
1002997

1003-
def iter_except(func, exception, first=None):
998+
def iter_except(function, exception, first=None):
1004999
"Convert a call-until-exception interface to an iterator interface."
10051000
# iter_except(d.popitem, KeyError) → non-blocking dictionary iterator
1006-
with contextlib.suppress(exception):
1001+
with suppress(exception):
10071002
if first is not None:
10081003
yield first()
10091004
while True:
1010-
yield func()
1005+
yield function()
10111006

10121007

10131008
The following recipes have a more mathematical flavor:
10141009

10151010
.. testcode::
10161011

10171012
def powerset(iterable):
1013+
"Subsequences of the iterable from shortest to longest."
10181014
# powerset([1,2,3]) → () (1,) (2,) (3,) (1,2) (1,3) (2,3) (1,2,3)
10191015
s = list(iterable)
10201016
return chain.from_iterable(combinations(s, r) for r in range(len(s)+1))
10211017

10221018
def sum_of_squares(iterable):
10231019
"Add up the squares of the input values."
10241020
# sum_of_squares([10, 20, 30]) → 1400
1025-
return math.sumprod(*tee(iterable))
1021+
return sumprod(*tee(iterable))
10261022
1027-
def reshape(matrix, cols):
1023+
def reshape(matrix, columns):
10281024
"Reshape a 2-D matrix to have a given number of columns."
10291025
# reshape([(0, 1), (2, 3), (4, 5)], 3) → (0, 1, 2), (3, 4, 5)
1030-
return batched(chain.from_iterable(matrix), cols, strict=True)
1026+
return batched(chain.from_iterable(matrix), columns, strict=True)
10311027

10321028
def transpose(matrix):
10331029
"Swap the rows and columns of a 2-D matrix."
@@ -1038,7 +1034,7 @@ The following recipes have a more mathematical flavor:
10381034
"Multiply two matrices."
10391035
# matmul([(7, 5), (3, 5)], [(2, 5), (7, 9)]) → (49, 80), (41, 60)
10401036
n = len(m2[0])
1041-
return batched(starmap(math.sumprod, product(m1, transpose(m2))), n)
1037+
return batched(starmap(sumprod, product(m1, transpose(m2))), n)
10421038

10431039
def convolve(signal, kernel):
10441040
"""Discrete linear convolution of two iterables.
@@ -1059,16 +1055,16 @@ The following recipes have a more mathematical flavor:
10591055
n = len(kernel)
10601056
padded_signal = chain(repeat(0, n-1), signal, repeat(0, n-1))
10611057
windowed_signal = sliding_window(padded_signal, n)
1062-
return map(math.sumprod, repeat(kernel), windowed_signal)
1058+
return map(sumprod, repeat(kernel), windowed_signal)
10631059

10641060
def polynomial_from_roots(roots):
10651061
"""Compute a polynomial's coefficients from its roots.
10661062

10671063
(x - 5) (x + 4) (x - 3) expands to: x³ -4x² -17x + 60
10681064
"""
10691065
# polynomial_from_roots([5, -4, 3]) → [1, -4, -17, 60]
1070-
factors = zip(repeat(1), map(operator.neg, roots))
1071-
return list(functools.reduce(convolve, factors, [1]))
1066+
factors = zip(repeat(1), map(neg, roots))
1067+
return list(reduce(convolve, factors, [1]))
10721068

10731069
def polynomial_eval(coefficients, x):
10741070
"""Evaluate a polynomial at a specific value.
@@ -1081,7 +1077,7 @@ The following recipes have a more mathematical flavor:
10811077
if not n:
10821078
return type(x)(0)
10831079
powers = map(pow, repeat(x), reversed(range(n)))
1084-
return math.sumprod(coefficients, powers)
1080+
return sumprod(coefficients, powers)
10851081

10861082
def polynomial_derivative(coefficients):
10871083
"""Compute the first derivative of a polynomial.
@@ -1092,15 +1088,15 @@ The following recipes have a more mathematical flavor:
10921088
# polynomial_derivative([1, -4, -17, 60]) → [3, -8, -17]
10931089
n = len(coefficients)
10941090
powers = reversed(range(1, n))
1095-
return list(map(operator.mul, coefficients, powers))
1091+
return list(map(mul, coefficients, powers))
10961092

10971093
def sieve(n):
10981094
"Primes less than n."
10991095
# sieve(30) → 2 3 5 7 11 13 17 19 23 29
11001096
if n > 2:
11011097
yield 2
11021098
data = bytearray((0, 1)) * (n // 2)
1103-
for p in iter_index(data, 1, start=3, stop=math.isqrt(n) + 1):
1099+
for p in iter_index(data, 1, start=3, stop=isqrt(n) + 1):
11041100
data[p*p : n : p+p] = bytes(len(range(p*p, n, p+p)))
11051101
yield from iter_index(data, 1, start=3)
11061102

@@ -1109,7 +1105,7 @@ The following recipes have a more mathematical flavor:
11091105
# factor(99) → 3 3 11
11101106
# factor(1_000_000_000_000_007) → 47 59 360620266859
11111107
# factor(1_000_000_000_000_403) → 1000000000000403
1112-
for prime in sieve(math.isqrt(n) + 1):
1108+
for prime in sieve(isqrt(n) + 1):
11131109
while not n % prime:
11141110
yield prime
11151111
n //= prime
@@ -1740,7 +1736,7 @@ The following recipes have a more mathematical flavor:
17401736

17411737
# Old recipes and their tests which are guaranteed to continue to work.
17421738

1743-
def sumprod(vec1, vec2):
1739+
def old_sumprod_recipe(vec1, vec2):
17441740
"Compute a sum of products."
17451741
return sum(starmap(operator.mul, zip(vec1, vec2, strict=True)))
17461742

@@ -1823,7 +1819,7 @@ The following recipes have a more mathematical flavor:
18231819
32
18241820

18251821

1826-
>>> sumprod([1,2,3], [4,5,6])
1822+
>>> old_sumprod_recipe([1,2,3], [4,5,6])
18271823
32
18281824

18291825

0 commit comments

Comments
 (0)