mirror of https://github.com/python/cpython
Misc minor improvements to the itertools recipes (gh-113477)
This commit is contained in:
parent
48c49739f5
commit
b5dc0f83ad
|
@ -803,11 +803,11 @@ which incur interpreter overhead.
|
|||
import random
|
||||
|
||||
def take(n, iterable):
|
||||
"Return first n items of the iterable as a list"
|
||||
"Return first n items of the iterable as a list."
|
||||
return list(islice(iterable, n))
|
||||
|
||||
def prepend(value, iterable):
|
||||
"Prepend a single value in front of an iterable"
|
||||
"Prepend a single value in front of an iterable."
|
||||
# prepend(1, [2, 3, 4]) --> 1 2 3 4
|
||||
return chain([value], iterable)
|
||||
|
||||
|
@ -825,15 +825,15 @@ which incur interpreter overhead.
|
|||
return starmap(func, repeat(args, times))
|
||||
|
||||
def flatten(list_of_lists):
|
||||
"Flatten one level of nesting"
|
||||
"Flatten one level of nesting."
|
||||
return chain.from_iterable(list_of_lists)
|
||||
|
||||
def ncycles(iterable, n):
|
||||
"Returns the sequence elements n times"
|
||||
"Returns the sequence elements n times."
|
||||
return chain.from_iterable(repeat(tuple(iterable), n))
|
||||
|
||||
def tail(n, iterable):
|
||||
"Return an iterator over the last n items"
|
||||
"Return an iterator over the last n items."
|
||||
# tail(3, 'ABCDEFG') --> E F G
|
||||
return iter(collections.deque(iterable, maxlen=n))
|
||||
|
||||
|
@ -848,7 +848,7 @@ which incur interpreter overhead.
|
|||
next(islice(iterator, n, n), None)
|
||||
|
||||
def nth(iterable, n, default=None):
|
||||
"Returns the nth item or a default value"
|
||||
"Returns the nth item or a default value."
|
||||
return next(islice(iterable, n, None), default)
|
||||
|
||||
def quantify(iterable, pred=bool):
|
||||
|
@ -856,7 +856,7 @@ which incur interpreter overhead.
|
|||
return sum(map(pred, iterable))
|
||||
|
||||
def all_equal(iterable):
|
||||
"Returns True if all the elements are equal to each other"
|
||||
"Returns True if all the elements are equal to each other."
|
||||
g = groupby(iterable)
|
||||
return next(g, True) and not next(g, False)
|
||||
|
||||
|
@ -873,6 +873,30 @@ which incur interpreter overhead.
|
|||
# first_true([a,b], x, f) --> a if f(a) else b if f(b) else x
|
||||
return next(filter(pred, iterable), default)
|
||||
|
||||
def unique_everseen(iterable, key=None):
|
||||
"List unique elements, preserving order. Remember all elements ever seen."
|
||||
# unique_everseen('AAAABBBCCDAABBB') --> A B C D
|
||||
# unique_everseen('ABBcCAD', str.casefold) --> A B c D
|
||||
seen = set()
|
||||
if key is None:
|
||||
for element in filterfalse(seen.__contains__, iterable):
|
||||
seen.add(element)
|
||||
yield element
|
||||
else:
|
||||
for element in iterable:
|
||||
k = key(element)
|
||||
if k not in seen:
|
||||
seen.add(k)
|
||||
yield element
|
||||
|
||||
def unique_justseen(iterable, key=None):
|
||||
"List unique elements, preserving order. Remember only the element just seen."
|
||||
# unique_justseen('AAAABBBCCDAABBB') --> A B C D A B
|
||||
# unique_justseen('ABBcCAD', str.casefold) --> A B c A D
|
||||
if key is None:
|
||||
return map(operator.itemgetter(0), groupby(iterable))
|
||||
return map(next, map(operator.itemgetter(1), groupby(iterable, key)))
|
||||
|
||||
def iter_index(iterable, value, start=0, stop=None):
|
||||
"Return indices where a value occurs in a sequence or iterable."
|
||||
# iter_index('AABCADEAF', 'A') --> 0 1 4 7
|
||||
|
@ -893,31 +917,17 @@ which incur interpreter overhead.
|
|||
except ValueError:
|
||||
pass
|
||||
|
||||
def iter_except(func, exception, first=None):
|
||||
""" Call a function repeatedly until an exception is raised.
|
||||
|
||||
Converts a call-until-exception interface to an iterator interface.
|
||||
Like builtins.iter(func, sentinel) but uses an exception instead
|
||||
of a sentinel to end the loop.
|
||||
|
||||
Examples:
|
||||
iter_except(functools.partial(heappop, h), IndexError) # priority queue iterator
|
||||
iter_except(d.popitem, KeyError) # non-blocking dict iterator
|
||||
iter_except(d.popleft, IndexError) # non-blocking deque iterator
|
||||
iter_except(q.get_nowait, Queue.Empty) # loop over a producer Queue
|
||||
iter_except(s.pop, KeyError) # non-blocking set iterator
|
||||
|
||||
"""
|
||||
try:
|
||||
if first is not None:
|
||||
yield first() # For database APIs needing an initial cast to db.first()
|
||||
while True:
|
||||
yield func()
|
||||
except exception:
|
||||
pass
|
||||
def sliding_window(iterable, n):
|
||||
"Collect data into overlapping fixed-length chunks or blocks."
|
||||
# sliding_window('ABCDEFG', 4) --> ABCD BCDE CDEF DEFG
|
||||
it = iter(iterable)
|
||||
window = collections.deque(islice(it, n-1), maxlen=n)
|
||||
for x in it:
|
||||
window.append(x)
|
||||
yield tuple(window)
|
||||
|
||||
def grouper(iterable, n, *, incomplete='fill', fillvalue=None):
|
||||
"Collect data into non-overlapping fixed-length chunks or blocks"
|
||||
"Collect data into non-overlapping fixed-length chunks or blocks."
|
||||
# grouper('ABCDEFG', 3, fillvalue='x') --> ABC DEF Gxx
|
||||
# grouper('ABCDEFG', 3, incomplete='strict') --> ABC DEF ValueError
|
||||
# grouper('ABCDEFG', 3, incomplete='ignore') --> ABC DEF
|
||||
|
@ -932,16 +942,9 @@ which incur interpreter overhead.
|
|||
case _:
|
||||
raise ValueError('Expected fill, strict, or ignore')
|
||||
|
||||
def sliding_window(iterable, n):
|
||||
# sliding_window('ABCDEFG', 4) --> ABCD BCDE CDEF DEFG
|
||||
it = iter(iterable)
|
||||
window = collections.deque(islice(it, n-1), maxlen=n)
|
||||
for x in it:
|
||||
window.append(x)
|
||||
yield tuple(window)
|
||||
|
||||
def roundrobin(*iterables):
|
||||
"roundrobin('ABC', 'D', 'EF') --> A D E B F C"
|
||||
"Visit input iterables in a cycle until each is exhausted."
|
||||
# roundrobin('ABC', 'D', 'EF') --> A D E B F C
|
||||
# Recipe credited to George Sakkis
|
||||
num_active = len(iterables)
|
||||
nexts = cycle(iter(it).__next__ for it in iterables)
|
||||
|
@ -964,11 +967,43 @@ which incur interpreter overhead.
|
|||
return filterfalse(pred, t1), filter(pred, t2)
|
||||
|
||||
def subslices(seq):
|
||||
"Return all contiguous non-empty subslices of a sequence"
|
||||
"Return all contiguous non-empty subslices of a sequence."
|
||||
# subslices('ABCD') --> A AB ABC ABCD B BC BCD C CD D
|
||||
slices = starmap(slice, combinations(range(len(seq) + 1), 2))
|
||||
return map(operator.getitem, repeat(seq), slices)
|
||||
|
||||
def iter_except(func, exception, first=None):
|
||||
""" Call a function repeatedly until an exception is raised.
|
||||
|
||||
Converts a call-until-exception interface to an iterator interface.
|
||||
Like builtins.iter(func, sentinel) but uses an exception instead
|
||||
of a sentinel to end the loop.
|
||||
|
||||
Priority queue iterator:
|
||||
iter_except(functools.partial(heappop, h), IndexError)
|
||||
|
||||
Non-blocking dictionary iterator:
|
||||
iter_except(d.popitem, KeyError)
|
||||
|
||||
Non-blocking deque iterator:
|
||||
iter_except(d.popleft, IndexError)
|
||||
|
||||
Non-blocking iterator over a producer Queue:
|
||||
iter_except(q.get_nowait, Queue.Empty)
|
||||
|
||||
Non-blocking set iterator:
|
||||
iter_except(s.pop, KeyError)
|
||||
|
||||
"""
|
||||
try:
|
||||
if first is not None:
|
||||
# For database APIs needing an initial call to db.first()
|
||||
yield first()
|
||||
while True:
|
||||
yield func()
|
||||
except exception:
|
||||
pass
|
||||
|
||||
def before_and_after(predicate, it):
|
||||
""" Variant of takewhile() that allows complete
|
||||
access to the remainder of the iterator.
|
||||
|
@ -980,12 +1015,12 @@ which incur interpreter overhead.
|
|||
>>> ''.join(remainder) # takewhile() would lose the 'd'
|
||||
'dEfGhI'
|
||||
|
||||
Note that the first iterator must be fully
|
||||
consumed before the second iterator can
|
||||
generate valid results.
|
||||
Note that the true iterator must be fully consumed
|
||||
before the remainder iterator can generate valid results.
|
||||
"""
|
||||
it = iter(it)
|
||||
transition = []
|
||||
|
||||
def true_iterator():
|
||||
for elem in it:
|
||||
if predicate(elem):
|
||||
|
@ -993,41 +1028,8 @@ which incur interpreter overhead.
|
|||
else:
|
||||
transition.append(elem)
|
||||
return
|
||||
def remainder_iterator():
|
||||
yield from transition
|
||||
yield from it
|
||||
return true_iterator(), remainder_iterator()
|
||||
|
||||
def unique_everseen(iterable, key=None):
|
||||
"List unique elements, preserving order. Remember all elements ever seen."
|
||||
# unique_everseen('AAAABBBCCDAABBB') --> A B C D
|
||||
# unique_everseen('ABBcCAD', str.lower) --> A B c D
|
||||
seen = set()
|
||||
if key is None:
|
||||
for element in filterfalse(seen.__contains__, iterable):
|
||||
seen.add(element)
|
||||
yield element
|
||||
# For order preserving deduplication,
|
||||
# a faster but non-lazy solution is:
|
||||
# yield from dict.fromkeys(iterable)
|
||||
else:
|
||||
for element in iterable:
|
||||
k = key(element)
|
||||
if k not in seen:
|
||||
seen.add(k)
|
||||
yield element
|
||||
# For use cases that allow the last matching element to be returned,
|
||||
# a faster but non-lazy solution is:
|
||||
# t1, t2 = tee(iterable)
|
||||
# yield from dict(zip(map(key, t1), t2)).values()
|
||||
|
||||
def unique_justseen(iterable, key=None):
|
||||
"List unique elements, preserving order. Remember only the element just seen."
|
||||
# unique_justseen('AAAABBBCCDAABBB') --> A B C D A B
|
||||
# unique_justseen('ABBcCAD', str.lower) --> A B c A D
|
||||
if key is None:
|
||||
return map(operator.itemgetter(0), groupby(iterable))
|
||||
return map(next, map(operator.itemgetter(1), groupby(iterable, key)))
|
||||
return true_iterator(), chain(transition, it)
|
||||
|
||||
|
||||
The following recipes have a more mathematical flavor:
|
||||
|
@ -1562,16 +1564,16 @@ The following recipes have a more mathematical flavor:
|
|||
|
||||
>>> list(unique_everseen('AAAABBBCCDAABBB'))
|
||||
['A', 'B', 'C', 'D']
|
||||
>>> list(unique_everseen('ABBCcAD', str.lower))
|
||||
>>> list(unique_everseen('ABBCcAD', str.casefold))
|
||||
['A', 'B', 'C', 'D']
|
||||
>>> list(unique_everseen('ABBcCAD', str.lower))
|
||||
>>> list(unique_everseen('ABBcCAD', str.casefold))
|
||||
['A', 'B', 'c', 'D']
|
||||
|
||||
>>> list(unique_justseen('AAAABBBCCDAABBB'))
|
||||
['A', 'B', 'C', 'D', 'A', 'B']
|
||||
>>> list(unique_justseen('ABBCcAD', str.lower))
|
||||
>>> list(unique_justseen('ABBCcAD', str.casefold))
|
||||
['A', 'B', 'C', 'A', 'D']
|
||||
>>> list(unique_justseen('ABBcCAD', str.lower))
|
||||
>>> list(unique_justseen('ABBcCAD', str.casefold))
|
||||
['A', 'B', 'c', 'A', 'D']
|
||||
|
||||
>>> d = dict(a=1, b=2, c=3)
|
||||
|
|
Loading…
Reference in New Issue