Misc improvement to the docs for itertools (gh-119529)

This commit is contained in:
Raymond Hettinger 2024-05-24 16:58:24 -05:00 committed by GitHub
parent 81d6336230
commit 49c3ade4f3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1 changed files with 54 additions and 54 deletions

View File

@ -134,7 +134,7 @@ loops that truncate the stream.
total = func(total, element)
yield total
There are a number of uses for the *func* argument. It can be set to
The *func* argument can be set to
:func:`min` for a running minimum, :func:`max` for a running maximum, or
:func:`operator.mul` for a running product. Amortization tables can be
built by accumulating interest and applying payments:
@ -184,21 +184,14 @@ loops that truncate the stream.
>>> unflattened
[('roses', 'red'), ('violets', 'blue'), ('sugar', 'sweet')]
>>> for batch in batched('ABCDEFG', 3):
... print(batch)
...
('A', 'B', 'C')
('D', 'E', 'F')
('G',)
Roughly equivalent to::
def batched(iterable, n, *, strict=False):
# batched('ABCDEFG', 3) → ABC DEF G
if n < 1:
raise ValueError('n must be at least one')
it = iter(iterable)
while batch := tuple(islice(it, n)):
iterable = iter(iterable)
while batch := tuple(islice(iterable, n)):
if strict and len(batch) != n:
raise ValueError('batched(): incomplete batch')
yield batch
@ -237,13 +230,13 @@ loops that truncate the stream.
Return *r* length subsequences of elements from the input *iterable*.
The combination tuples are emitted in lexicographic ordering according to
The combination tuples are emitted in lexicographic order according to
the order of the input *iterable*. So, if the input *iterable* is sorted,
the output tuples will be produced in sorted order.
Elements are treated as unique based on their position, not on their
value. So if the input elements are unique, there will be no repeated
values in each combination.
value. So, if the input elements are unique, there will be no repeated
values within each combination.
Roughly equivalent to::
@ -286,12 +279,12 @@ loops that truncate the stream.
Return *r* length subsequences of elements from the input *iterable*
allowing individual elements to be repeated more than once.
The combination tuples are emitted in lexicographic ordering according to
The combination tuples are emitted in lexicographic order according to
the order of the input *iterable*. So, if the input *iterable* is sorted,
the output tuples will be produced in sorted order.
Elements are treated as unique based on their position, not on their
value. So if the input elements are unique, the generated combinations
value. So, if the input elements are unique, the generated combinations
will also be unique.
Roughly equivalent to::
@ -332,13 +325,13 @@ loops that truncate the stream.
.. function:: compress(data, selectors)
Make an iterator that filters elements from *data* returning only those that
have a corresponding element in *selectors* that evaluates to ``True``.
Stops when either the *data* or *selectors* iterables has been exhausted.
have a corresponding element in *selectors* is true.
Stops when either the *data* or *selectors* iterables have been exhausted.
Roughly equivalent to::
def compress(data, selectors):
# compress('ABCDEF', [1,0,1,0,1,1]) → A C E F
return (d for d, s in zip(data, selectors) if s)
return (datum for datum, selector in zip(data, selectors) if selector)
.. versionadded:: 3.1
@ -392,7 +385,7 @@ loops that truncate the stream.
start-up time. Roughly equivalent to::
def dropwhile(predicate, iterable):
# dropwhile(lambda x: x<5, [1,4,6,4,1]) → 6 4 1
# dropwhile(lambda x: x<5, [1,4,6,3,8]) → 6 3 8
iterable = iter(iterable)
for x in iterable:
if not predicate(x):
@ -408,7 +401,7 @@ loops that truncate the stream.
that are false. Roughly equivalent to::
def filterfalse(predicate, iterable):
# filterfalse(lambda x: x%2, range(10)) → 0 2 4 6 8
# filterfalse(lambda x: x<5, [1,4,6,3,8]) → 6 8
if predicate is None:
predicate = bool
for x in iterable:
@ -444,36 +437,37 @@ loops that truncate the stream.
:func:`groupby` is roughly equivalent to::
class groupby:
def groupby(iterable, key=None):
# [k for k, g in groupby('AAAABBBCCDAABBB')] → A B C D A B
# [list(g) for k, g in groupby('AAAABBBCCD')] → AAAA BBB CC D
def __init__(self, iterable, key=None):
if key is None:
key = lambda x: x
self.keyfunc = key
self.it = iter(iterable)
self.tgtkey = self.currkey = self.currvalue = object()
keyfunc = (lambda x: x) if key is None else key
iterator = iter(iterable)
exhausted = False
def __iter__(self):
return self
def __next__(self):
self.id = object()
while self.currkey == self.tgtkey:
self.currvalue = next(self.it) # Exit on StopIteration
self.currkey = self.keyfunc(self.currvalue)
self.tgtkey = self.currkey
return (self.currkey, self._grouper(self.tgtkey, self.id))
def _grouper(self, tgtkey, id):
while self.id is id and self.currkey == tgtkey:
yield self.currvalue
try:
self.currvalue = next(self.it)
except StopIteration:
def _grouper(target_key):
nonlocal curr_value, curr_key, exhausted
yield curr_value
for curr_value in iterator:
curr_key = keyfunc(curr_value)
if curr_key != target_key:
return
self.currkey = self.keyfunc(self.currvalue)
yield curr_value
exhausted = True
try:
curr_value = next(iterator)
except StopIteration:
return
curr_key = keyfunc(curr_value)
while not exhausted:
target_key = curr_key
curr_group = _grouper(target_key)
yield curr_key, curr_group
if curr_key == target_key:
for _ in curr_group:
pass
.. function:: islice(iterable, stop)
@ -501,13 +495,15 @@ loops that truncate the stream.
# islice('ABCDEFG', 2, 4) → C D
# islice('ABCDEFG', 2, None) → C D E F G
# islice('ABCDEFG', 0, None, 2) → A C E G
s = slice(*args)
start = 0 if s.start is None else s.start
stop = s.stop
step = 1 if s.step is None else s.step
if start < 0 or (stop is not None and stop < 0) or step <= 0:
raise ValueError
indices = count() if stop is None else range(max(stop, start))
indices = count() if stop is None else range(max(start, stop))
next_i = start
for i, element in zip(indices, iterable):
if i == next_i:
@ -549,7 +545,7 @@ loops that truncate the stream.
the output tuples will be produced in sorted order.
Elements are treated as unique based on their position, not on their
value. So if the input elements are unique, there will be no repeated
value. So, if the input elements are unique, there will be no repeated
values within a permutation.
Roughly equivalent to::
@ -557,14 +553,17 @@ loops that truncate the stream.
def permutations(iterable, r=None):
# permutations('ABCD', 2) → AB AC AD BA BC BD CA CB CD DA DB DC
# permutations(range(3)) → 012 021 102 120 201 210
pool = tuple(iterable)
n = len(pool)
r = n if r is None else r
if r > n:
return
indices = list(range(n))
cycles = list(range(n, n-r, -1))
yield tuple(pool[i] for i in indices[:r])
while n:
for i in reversed(range(r)):
cycles[i] -= 1
@ -580,7 +579,7 @@ loops that truncate the stream.
return
The code for :func:`permutations` can be also expressed as a subsequence of
:func:`product`, filtered to exclude entries with repeated elements (those
:func:`product` filtered to exclude entries with repeated elements (those
from the same position in the input pool)::
def permutations(iterable, r=None):
@ -674,17 +673,16 @@ loops that truncate the stream.
predicate is true. Roughly equivalent to::
def takewhile(predicate, iterable):
# takewhile(lambda x: x<5, [1,4,6,4,1]) → 1 4
# takewhile(lambda x: x<5, [1,4,6,3,8]) → 1 4
for x in iterable:
if predicate(x):
yield x
else:
if not predicate(x):
break
yield x
Note, the element that first fails the predicate condition is
consumed from the input iterator and there is no way to access it.
This could be an issue if an application wants to further consume the
input iterator after takewhile has been run to exhaustion. To work
input iterator after *takewhile* has been run to exhaustion. To work
around this problem, consider using `more-iterools before_and_after()
<https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.before_and_after>`_
instead.
@ -734,10 +732,12 @@ loops that truncate the stream.
def zip_longest(*iterables, fillvalue=None):
# zip_longest('ABCD', 'xy', fillvalue='-') → Ax By C- D-
iterators = [iter(it) for it in iterables]
iterators = list(map(iter, iterables))
num_active = len(iterators)
if not num_active:
return
while True:
values = []
for i, iterator in enumerate(iterators):