Skip to content

Commit 574bdb6

Browse files
committed
Misc improvement to the docs for itertools (pythongh-119529)
1 parent 30679a6 commit 574bdb6

1 file changed

Lines changed: 57 additions & 55 deletions

File tree

Doc/library/itertools.rst

Lines changed: 57 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ loops that truncate the stream.
134134
total = func(total, element)
135135
yield total
136136

137-
There are a number of uses for the *func* argument. It can be set to
137+
The *func* argument can be set to
138138
:func:`min` for a running minimum, :func:`max` for a running maximum, or
139139
:func:`operator.mul` for a running product. Amortization tables can be
140140
built by accumulating interest and applying payments:
@@ -181,21 +181,16 @@ loops that truncate the stream.
181181
>>> unflattened
182182
[('roses', 'red'), ('violets', 'blue'), ('sugar', 'sweet')]
183183

184-
>>> for batch in batched('ABCDEFG', 3):
185-
... print(batch)
186-
...
187-
('A', 'B', 'C')
188-
('D', 'E', 'F')
189-
('G',)
190-
191184
Roughly equivalent to::
192185

193186
def batched(iterable, n):
194187
# batched('ABCDEFG', 3) → ABC DEF G
195188
if n < 1:
196189
raise ValueError('n must be at least one')
197-
it = iter(iterable)
198-
while batch := tuple(islice(it, n)):
190+
iterable = iter(iterable)
191+
while batch := tuple(islice(iterable, n)):
192+
if strict and len(batch) != n:
193+
raise ValueError('batched(): incomplete batch')
199194
yield batch
200195

201196
.. versionadded:: 3.12
@@ -229,13 +224,13 @@ loops that truncate the stream.
229224

230225
Return *r* length subsequences of elements from the input *iterable*.
231226

232-
The combination tuples are emitted in lexicographic ordering according to
227+
The combination tuples are emitted in lexicographic order according to
233228
the order of the input *iterable*. So, if the input *iterable* is sorted,
234229
the output tuples will be produced in sorted order.
235230

236231
Elements are treated as unique based on their position, not on their
237-
value. So if the input elements are unique, there will be no repeated
238-
values in each combination.
232+
value. So, if the input elements are unique, there will be no repeated
233+
values within each combination.
239234

240235
Roughly equivalent to::
241236

@@ -278,12 +273,12 @@ loops that truncate the stream.
278273
Return *r* length subsequences of elements from the input *iterable*
279274
allowing individual elements to be repeated more than once.
280275

281-
The combination tuples are emitted in lexicographic ordering according to
276+
The combination tuples are emitted in lexicographic order according to
282277
the order of the input *iterable*. So, if the input *iterable* is sorted,
283278
the output tuples will be produced in sorted order.
284279

285280
Elements are treated as unique based on their position, not on their
286-
value. So if the input elements are unique, the generated combinations
281+
value. So, if the input elements are unique, the generated combinations
287282
will also be unique.
288283

289284
Roughly equivalent to::
@@ -324,13 +319,13 @@ loops that truncate the stream.
324319
.. function:: compress(data, selectors)
325320

326321
Make an iterator that filters elements from *data* returning only those that
327-
have a corresponding element in *selectors* that evaluates to ``True``.
328-
Stops when either the *data* or *selectors* iterables has been exhausted.
322+
have a corresponding element in *selectors* is true.
323+
Stops when either the *data* or *selectors* iterables have been exhausted.
329324
Roughly equivalent to::
330325

331326
def compress(data, selectors):
332327
# compress('ABCDEF', [1,0,1,0,1,1]) → A C E F
333-
return (d for d, s in zip(data, selectors) if s)
328+
return (datum for datum, selector in zip(data, selectors) if selector)
334329

335330
.. versionadded:: 3.1
336331

@@ -384,7 +379,7 @@ loops that truncate the stream.
384379
start-up time. Roughly equivalent to::
385380

386381
def dropwhile(predicate, iterable):
387-
# dropwhile(lambda x: x<5, [1,4,6,4,1]) → 6 4 1
382+
# dropwhile(lambda x: x<5, [1,4,6,3,8]) → 6 3 8
388383
iterable = iter(iterable)
389384
for x in iterable:
390385
if not predicate(x):
@@ -400,7 +395,7 @@ loops that truncate the stream.
400395
that are false. Roughly equivalent to::
401396

402397
def filterfalse(predicate, iterable):
403-
# filterfalse(lambda x: x%2, range(10)) → 0 2 4 6 8
398+
# filterfalse(lambda x: x<5, [1,4,6,3,8]) → 6 8
404399
if predicate is None:
405400
predicate = bool
406401
for x in iterable:
@@ -436,36 +431,37 @@ loops that truncate the stream.
436431

437432
:func:`groupby` is roughly equivalent to::
438433

439-
class groupby:
434+
def groupby(iterable, key=None):
440435
# [k for k, g in groupby('AAAABBBCCDAABBB')] → A B C D A B
441436
# [list(g) for k, g in groupby('AAAABBBCCD')] → AAAA BBB CC D
442437

443-
def __init__(self, iterable, key=None):
444-
if key is None:
445-
key = lambda x: x
446-
self.keyfunc = key
447-
self.it = iter(iterable)
448-
self.tgtkey = self.currkey = self.currvalue = object()
449-
450-
def __iter__(self):
451-
return self
452-
453-
def __next__(self):
454-
self.id = object()
455-
while self.currkey == self.tgtkey:
456-
self.currvalue = next(self.it) # Exit on StopIteration
457-
self.currkey = self.keyfunc(self.currvalue)
458-
self.tgtkey = self.currkey
459-
return (self.currkey, self._grouper(self.tgtkey, self.id))
460-
461-
def _grouper(self, tgtkey, id):
462-
while self.id is id and self.currkey == tgtkey:
463-
yield self.currvalue
464-
try:
465-
self.currvalue = next(self.it)
466-
except StopIteration:
438+
keyfunc = (lambda x: x) if key is None else key
439+
iterator = iter(iterable)
440+
exhausted = False
441+
442+
def _grouper(target_key):
443+
nonlocal curr_value, curr_key, exhausted
444+
yield curr_value
445+
for curr_value in iterator:
446+
curr_key = keyfunc(curr_value)
447+
if curr_key != target_key:
467448
return
468-
self.currkey = self.keyfunc(self.currvalue)
449+
yield curr_value
450+
exhausted = True
451+
452+
try:
453+
curr_value = next(iterator)
454+
except StopIteration:
455+
return
456+
curr_key = keyfunc(curr_value)
457+
458+
while not exhausted:
459+
target_key = curr_key
460+
curr_group = _grouper(target_key)
461+
yield curr_key, curr_group
462+
if curr_key == target_key:
463+
for _ in curr_group:
464+
pass
469465

470466

471467
.. function:: islice(iterable, stop)
@@ -493,13 +489,15 @@ loops that truncate the stream.
493489
# islice('ABCDEFG', 2, 4) → C D
494490
# islice('ABCDEFG', 2, None) → C D E F G
495491
# islice('ABCDEFG', 0, None, 2) → A C E G
492+
496493
s = slice(*args)
497494
start = 0 if s.start is None else s.start
498495
stop = s.stop
499496
step = 1 if s.step is None else s.step
500497
if start < 0 or (stop is not None and stop < 0) or step <= 0:
501498
raise ValueError
502-
indices = count() if stop is None else range(max(stop, start))
499+
500+
indices = count() if stop is None else range(max(start, stop))
503501
next_i = start
504502
for i, element in zip(indices, iterable):
505503
if i == next_i:
@@ -541,22 +539,25 @@ loops that truncate the stream.
541539
the output tuples will be produced in sorted order.
542540

543541
Elements are treated as unique based on their position, not on their
544-
value. So if the input elements are unique, there will be no repeated
542+
value. So, if the input elements are unique, there will be no repeated
545543
values within a permutation.
546544

547545
Roughly equivalent to::
548546

549547
def permutations(iterable, r=None):
550548
# permutations('ABCD', 2) → AB AC AD BA BC BD CA CB CD DA DB DC
551549
# permutations(range(3)) → 012 021 102 120 201 210
550+
552551
pool = tuple(iterable)
553552
n = len(pool)
554553
r = n if r is None else r
555554
if r > n:
556555
return
556+
557557
indices = list(range(n))
558558
cycles = list(range(n, n-r, -1))
559559
yield tuple(pool[i] for i in indices[:r])
560+
560561
while n:
561562
for i in reversed(range(r)):
562563
cycles[i] -= 1
@@ -572,7 +573,7 @@ loops that truncate the stream.
572573
return
573574

574575
The code for :func:`permutations` can be also expressed as a subsequence of
575-
:func:`product`, filtered to exclude entries with repeated elements (those
576+
:func:`product` filtered to exclude entries with repeated elements (those
576577
from the same position in the input pool)::
577578

578579
def permutations(iterable, r=None):
@@ -666,17 +667,16 @@ loops that truncate the stream.
666667
predicate is true. Roughly equivalent to::
667668

668669
def takewhile(predicate, iterable):
669-
# takewhile(lambda x: x<5, [1,4,6,4,1]) → 1 4
670+
# takewhile(lambda x: x<5, [1,4,6,3,8]) → 1 4
670671
for x in iterable:
671-
if predicate(x):
672-
yield x
673-
else:
672+
if not predicate(x):
674673
break
674+
yield x
675675

676676
Note, the element that first fails the predicate condition is
677677
consumed from the input iterator and there is no way to access it.
678678
This could be an issue if an application wants to further consume the
679-
input iterator after takewhile has been run to exhaustion. To work
679+
input iterator after *takewhile* has been run to exhaustion. To work
680680
around this problem, consider using `more-iterools before_and_after()
681681
<https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.before_and_after>`_
682682
instead.
@@ -726,10 +726,12 @@ loops that truncate the stream.
726726

727727
def zip_longest(*iterables, fillvalue=None):
728728
# zip_longest('ABCD', 'xy', fillvalue='-') → Ax By C- D-
729-
iterators = [iter(it) for it in iterables]
729+
730+
iterators = list(map(iter, iterables))
730731
num_active = len(iterators)
731732
if not num_active:
732733
return
734+
733735
while True:
734736
values = []
735737
for i, iterator in enumerate(iterators):

0 commit comments

Comments
 (0)