reiter module

Wrapper for Python iterators and iterables that implements a list-like random-access interface by caching retrieved items for later reuse.

class reiter.reiter.reiter(iterable: Iterable)[source]

Bases: collections.abc.Iterator, collections.abc.Iterable

Wrapper class for iterators and iterables that provides an interface enabling repeated iteration and random access by index of the sequence of items contained within.

static __new__(cls, iterable: Iterable)[source]

Constructor that wraps an iterator or iterable. An instance of this class yields the same sequence of items as the wrapped object.

>>> xs = iter([1, 2, 3])
>>> ys = reiter(xs)
>>> list(ys)
[1, 2, 3]

Unlike iterators and some iterable objects (including those that are built-in and those that are user-defined), an instance of this class always allows iteration over its items any number of times.

>>> list(ys), list(ys)
([1, 2, 3], [1, 2, 3])

Furthermore, it is also possible to access elements using their index.

>>> xs = iter([1, 2, 3])
>>> ys = reiter(xs)
>>> ys[0], ys[1], ys[2]
(1, 2, 3)

An instance of this class can be constructed from another instance of this class.

>>> list(reiter(reiter(iter([1, 2, 3]))))
[1, 2, 3]

The type of an instance of this class can be checked in the usual manner, and an instance of this class cannot be constructed from a value or object that is not an iterator or iterable.

>>> isinstance(reiter(xs), reiter)
True
>>> reiter(123)
Traceback (most recent call last):
  ...
TypeError: supplied object is not iterable
__next__() Any[source]

Substitute definition of the corresponding method for iterators that also caches the retrieved item before returning it.

>>> xs = reiter(iter([1, 2, 3]))
>>> next(xs), next(xs), next(xs)
(1, 2, 3)

Any attempt to retrieve items once the sequence of items is exhausted raises an exception in the usual manner.

>>> next(xs)
Traceback (most recent call last):
  ...
StopIteration

However, all items yielded during iteration can be accessed by their index, and it is also possible to iterate over them again.

>>> xs[0], xs[1], xs[2]
(1, 2, 3)
>>> [x for x in xs]
[1, 2, 3]
>>> [x for x in xs], [x for x in xs]
([1, 2, 3], [1, 2, 3])
__getitem__(index: Union[int, slice]) Any[source]

Returns the item at the supplied index or the items within the range of the supplied slice, retrieving additional items from the iterator (and caching them) as necessary.

>>> xs = reiter(iter([1, 2, 3]))
>>> xs[2]
3
>>> xs[1]
2
>>> xs = reiter(range(10))
>>> xs[0]
0
>>> xs = reiter(range(10))
>>> xs[10]
Traceback (most recent call last):
  ...
IndexError: index out of range
>>> xs['abc']
Traceback (most recent call last):
  ...
ValueError: index must be integer or slice

Use of slice notation is supported, but it should be used carefully. Omitting a lower or upper bound may require retrieving (and caching) all items.

>>> xs = reiter(iter([1, 2, 3]))
>>> xs[0:2]
[1, 2]
>>> xs = reiter(iter([1, 2, 3]))
>>> xs[:2]
[1, 2]
>>> xs = reiter(iter([1, 2, 3]))
>>> xs[0:]
[1, 2, 3]
>>> xs = reiter(iter([1, 2, 3]))
>>> xs[:]
[1, 2, 3]
>>> xs = reiter(iter([1, 2, 3]))
>>> xs[2:0:-1]
[3, 2]
>>> xs = reiter(iter([1, 2, 3]))
>>> xs[2::-1]
[3, 2, 1]
>>> xs = reiter(iter([1, 2, 3]))
>>> xs[::-1]
[3, 2, 1]
>>> xs = reiter(iter([1, 2, 3]))
>>> xs[:0:-1]
[3, 2]
__iter__() Iterable[source]

Builds a new iterator that begins at the first cached element and continues from there. This method is an effective way to “reset” the instance of this class so that the built-in next function can be used again.

>>> xs = reiter(iter([1, 2, 3]))
>>> next(xs)
1
>>> next(xs)
2
>>> next(xs)
3
>>> next(xs)
Traceback (most recent call last):
  ...
StopIteration
>>> xs = iter(xs)
>>> next(xs), next(xs), next(xs)
(1, 2, 3)
has(index: Optional[int] = None) bool[source]

Returns a boolean indicating whether a next item is available, or if an item exists at the specified index.

>>> xs = reiter(iter([1, 2, 3]))
>>> xs.has(), xs.has(), xs.has(), xs.has()
(True, True, True, False)

If an explicit index is supplied, a boolean value is returned indicating whether an item exists at that position in the sequence within the wrapped iterator or iterable.

>>> xs.has(2)
True
>>> xs = reiter(iter([1, 2, 3]))
>>> xs.has(2)
True
>>> xs.has(3)
False
length() Optional[int][source]

Returns the length of this instance, if all items have been retrieved. If not all items have been retrieved, None is returned.

>>> xs = reiter(iter([1, 2, 3]))
>>> xs.length() is None
True
>>> next(xs)
1
>>> xs.length() is None
True
>>> next(xs), next(xs)
(2, 3)
>>> next(xs)
Traceback (most recent call last):
  ...
StopIteration
>>> xs.length()
3

Invoking the has method until the instance is exhausted is sufficient to ensure that all items have been retrieved.

>>> xs = reiter(iter([1, 2, 3]))
>>> xs.has(), xs.has(), xs.has(), xs.has()
(True, True, True, False)
>>> xs.length()
3