From a014894f1397c302dfb1e95cd41f5f0b5124a5a0 Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Mon, 13 May 2024 08:46:31 -0500 Subject: [PATCH 1/7] Remove self-evident comment in iter_index --- Doc/library/itertools.rst | 2 -- 1 file changed, 2 deletions(-) diff --git a/Doc/library/itertools.rst b/Doc/library/itertools.rst index afb7a6e835a220..2ea71671a40b8f 100644 --- a/Doc/library/itertools.rst +++ b/Doc/library/itertools.rst @@ -942,13 +942,11 @@ and :term:`generators ` which incur interpreter overhead. # iter_index('AABCADEAF', 'A') → 0 1 4 7 seq_index = getattr(iterable, 'index', None) if seq_index is None: - # Path for general iterables iterator = islice(iterable, start, stop) for i, element in enumerate(iterator, start): if element is value or element == value: yield i else: - # Path for sequences with an index() method stop = len(iterable) if stop is None else stop i = start try: From c4bfbfb252b9162fb79a09d23793f9f2b4998164 Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Mon, 13 May 2024 09:25:34 -0500 Subject: [PATCH 2/7] Use contextlib.suppress() instead of try/except --- Doc/library/itertools.rst | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/Doc/library/itertools.rst b/Doc/library/itertools.rst index 2ea71671a40b8f..fc4d83119ffa67 100644 --- a/Doc/library/itertools.rst +++ b/Doc/library/itertools.rst @@ -800,6 +800,7 @@ and :term:`generators ` which incur interpreter overhead. .. testcode:: import collections + import contextlib import functools import math import operator @@ -949,23 +950,19 @@ and :term:`generators ` which incur interpreter overhead. else: stop = len(iterable) if stop is None else stop i = start - try: + with contextlib.suppress(ValueError): while True: yield (i := seq_index(value, i, stop)) i += 1 - except ValueError: - pass def iter_except(func, exception, first=None): "Convert a call-until-exception interface to an iterator interface." # iter_except(d.popitem, KeyError) → non-blocking dictionary iterator - try: + with contextlib.suppress(exception): if first is not None: yield first() while True: yield func() - except exception: - pass The following recipes have a more mathematical flavor: From 7b825438fea85437ad749fce70d04a4ff378851f Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Mon, 13 May 2024 09:42:12 -0500 Subject: [PATCH 3/7] Simplify with "yield from" --- Doc/library/itertools.rst | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Doc/library/itertools.rst b/Doc/library/itertools.rst index fc4d83119ffa67..4cfa12f89199f0 100644 --- a/Doc/library/itertools.rst +++ b/Doc/library/itertools.rst @@ -218,9 +218,8 @@ loops that truncate the stream. def chain(*iterables): # chain('ABC', 'DEF') → A B C D E F - for it in iterables: - for element in it: - yield element + for iterable in iterables: + yield from iterable .. classmethod:: chain.from_iterable(iterable) From 0eb2662f200bee25f501c4b93a5c80515e69ce87 Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Mon, 13 May 2024 09:43:02 -0500 Subject: [PATCH 4/7] Simplify with "yield from" --- Doc/library/itertools.rst | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Doc/library/itertools.rst b/Doc/library/itertools.rst index 4cfa12f89199f0..b7d28091a6b73d 100644 --- a/Doc/library/itertools.rst +++ b/Doc/library/itertools.rst @@ -229,9 +229,8 @@ loops that truncate the stream. def from_iterable(iterables): # chain.from_iterable(['ABC', 'DEF']) → A B C D E F - for it in iterables: - for element in it: - yield element + for iterable in iterables: + yield from iterable .. function:: combinations(iterable, r) From 83f367cffe6291715f4dcc1ca24bf3ddae7c82c5 Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Mon, 13 May 2024 10:19:12 -0500 Subject: [PATCH 5/7] Replace "it" with "iterable" in places where it helps. --- Doc/library/itertools.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Doc/library/itertools.rst b/Doc/library/itertools.rst index b7d28091a6b73d..51ae2c9f531f02 100644 --- a/Doc/library/itertools.rst +++ b/Doc/library/itertools.rst @@ -122,15 +122,15 @@ loops that truncate the stream. # accumulate([1,2,3,4,5]) → 1 3 6 10 15 # accumulate([1,2,3,4,5], initial=100) → 100 101 103 106 110 115 # accumulate([1,2,3,4,5], operator.mul) → 1 2 6 24 120 - it = iter(iterable) + iterator = iter(iterable) total = initial if initial is None: try: - total = next(it) + total = next(iterator) except StopIteration: return yield total - for element in it: + for element in iterator: total = func(total, element) yield total @@ -741,9 +741,9 @@ loops that truncate the stream. return while True: values = [] - for i, it in enumerate(iterators): + for i, iterator in enumerate(iterators): try: - value = next(it) + value = next(iterator) except StopIteration: num_active -= 1 if not num_active: From 206cd5e9e5c893bb55fae15b682fe66d69c419d7 Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Tue, 14 May 2024 09:22:01 -0500 Subject: [PATCH 6/7] More accurate tee() equivalent with a shared data store --- Doc/library/itertools.rst | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/Doc/library/itertools.rst b/Doc/library/itertools.rst index 51ae2c9f531f02..587a9a1d7b5d56 100644 --- a/Doc/library/itertools.rst +++ b/Doc/library/itertools.rst @@ -694,24 +694,22 @@ loops that truncate the stream. Return *n* independent iterators from a single iterable. - The following Python code helps explain what *tee* does (although the actual - implementation is more complex and uses only a single underlying - :abbr:`FIFO (first-in, first-out)` queue):: + Roughly equivalent to:: def tee(iterable, n=2): - it = iter(iterable) - deques = [collections.deque() for i in range(n)] - def gen(mydeque): - while True: - if not mydeque: # when the local deque is empty - try: - newval = next(it) # fetch a new value and - except StopIteration: - return - for d in deques: # load it to all the deques - d.append(newval) - yield mydeque.popleft() - return tuple(gen(d) for d in deques) + iterator = iter(iterable) + empty_link = [None, None] # Singly linked list: [value, link] + return tuple(_tee(iterator, empty_link) for _ in range(n)) + + def _tee(iterator, link): + while True: + if link[1] is None: + try: + link[:] = [next(iterator), [None, None]] + except StopIteration: + break + value, link = link + yield value Once a :func:`tee` has been created, the original *iterable* should not be used anywhere else; otherwise, the *iterable* could get advanced without From 4f2b6a82db83cf8a02b8f08c60c0c91a05c12959 Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Tue, 14 May 2024 09:57:28 -0500 Subject: [PATCH 7/7] Match the "return" style of the other equivalents --- Doc/library/itertools.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/itertools.rst b/Doc/library/itertools.rst index 587a9a1d7b5d56..a19baa3f0e439f 100644 --- a/Doc/library/itertools.rst +++ b/Doc/library/itertools.rst @@ -707,7 +707,7 @@ loops that truncate the stream. try: link[:] = [next(iterator), [None, None]] except StopIteration: - break + return value, link = link yield value