From 2afba5ca6d27c69bc0a1618e86c2e4c00eefa4c1 Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Mon, 9 Sep 2024 20:57:49 -0500 Subject: [PATCH] Small improvements to the itertools docs (GH-123885) --- Doc/library/itertools.rst | 16 +++++++++++++++- Lib/test/test_itertools.py | 18 +++++++++++------- 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/Doc/library/itertools.rst b/Doc/library/itertools.rst index 553abf788b223a..508c20f4df6f5e 100644 --- a/Doc/library/itertools.rst +++ b/Doc/library/itertools.rst @@ -474,7 +474,7 @@ loops that truncate the stream. If *start* is zero or ``None``, iteration starts at zero. Otherwise, elements from the iterable are skipped until *start* is reached. - If *stop* is ``None``, iteration continues until the iterator is + If *stop* is ``None``, iteration continues until the iterable is exhausted, if at all. Otherwise, it stops at the specified position. If *step* is ``None``, the step defaults to one. Elements are returned @@ -503,6 +503,10 @@ loops that truncate the stream. yield element next_i += step + If the input is an iterator, then fully consuming the *islice* + advances the input iterator by ``max(start, stop)`` steps regardless + of the *step* value. + .. function:: pairwise(iterable) @@ -601,6 +605,8 @@ loops that truncate the stream. # product('ABCD', 'xy') → Ax Ay Bx By Cx Cy Dx Dy # product(range(2), repeat=3) → 000 001 010 011 100 101 110 111 + if repeat < 0: + raise ValueError('repeat argument cannot be negative') pools = [tuple(pool) for pool in iterables] * repeat result = [[]] @@ -684,6 +690,8 @@ loops that truncate the stream. Roughly equivalent to:: def tee(iterable, n=2): + if n < 0: + raise ValueError('n must be >= 0') iterator = iter(iterable) shared_link = [None, None] return tuple(_tee(iterator, shared_link) for _ in range(n)) @@ -703,6 +711,12 @@ loops that truncate the stream. used anywhere else; otherwise, the *iterable* could get advanced without the tee objects being informed. + When the input *iterable* is already a tee iterator object, all + members of the return tuple are constructed as if they had been + produced by the upstream :func:`tee` call. This "flattening step" + 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. + ``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. diff --git a/Lib/test/test_itertools.py b/Lib/test/test_itertools.py index 9c0c4b4de18cf1..6820dce3f12620 100644 --- a/Lib/test/test_itertools.py +++ b/Lib/test/test_itertools.py @@ -992,12 +992,16 @@ def product1(*args, **kwds): else: return - def product2(*args, **kwds): + def product2(*iterables, repeat=1): 'Pure python version used in docs' - pools = list(map(tuple, args)) * kwds.get('repeat', 1) + if repeat < 0: + raise ValueError('repeat argument cannot be negative') + pools = [tuple(pool) for pool in iterables] * repeat + result = [[]] for pool in pools: result = [x+[y] for x in result for y in pool] + for prod in result: yield tuple(prod) @@ -1754,6 +1758,8 @@ def test_tee_recipe(self): # Begin tee() recipe ########################################### def tee(iterable, n=2): + if n < 0: + raise ValueError('n must be >= 0') iterator = iter(iterable) shared_link = [None, None] return tuple(_tee(iterator, shared_link) for _ in range(n)) @@ -1829,11 +1835,9 @@ def _tee(iterator, link): self.assertEqual(list(a), list(range(100,2000))) self.assertEqual(list(c), list(range(2,2000))) - # Tests not applicable to the tee() recipe - if False: - # test invalid values of n - self.assertRaises(TypeError, tee, 'abc', 'invalid') - self.assertRaises(ValueError, tee, [], -1) + # test invalid values of n + self.assertRaises(TypeError, tee, 'abc', 'invalid') + self.assertRaises(ValueError, tee, [], -1) for n in range(5): result = tee('abc', n)