"""
Common patterns for working with :term:`iterable` objects
"""
import collections.abc
import itertools
import typing
[docs]def chunks(input_: typing.Iterable, chunk_size: int):
"""
create an iterator that splits `input_` into objects of length `chunk_size`
if `input_` is a `typing.Sequence`, the generated chunks will be
slices of `input_`. Otherwise, the generated chunks will
themselves be independent generators on `input_`.
The last chunk will contain :code:`len(input_) % chunk_size` elements
.. doctest::
:options: +NORMALIZE_WHITESPACE
>>> from fictive.patterns.iterables import chunks
>>> sample = 'spam and eggs'
>>> chunks(sample, 5)
<generator object chunks at ...>
>>> for chunk in chunks(sample, 5):
... print(chunk)
spam
and e
ggs
>>> sample = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
>>> chunk_generators = list(chunks(sample, 4))
>>> chunk_generators
[<itertools.islice object at ...>, \
<itertools.islice object at ...>, \
<itertools.islice object at ...>]
>>> list(chunk_generators[2])
[9, 10]
>>> list(chunk_generators[0])
[1, 2, 3, 4]
"""
if isinstance(input_, collections.abc.Sequence):
while input_:
chunk, input_ = input_[:chunk_size], input_[chunk_size:]
yield chunk
elif isinstance(input_, collections.abc.Iterator):
try:
while True:
chunk, input_, test = itertools.tee(input_, 3)
next(test)
chunk = itertools.islice(chunk, 0, chunk_size)
input_ = itertools.islice(input_, chunk_size, None)
yield chunk
except StopIteration:
return
else:
for chunk in chunks((item for item in input_), chunk_size):
yield chunk