For a custom container wrapping an iterator, I intended to delegate some of my logic to itertools.islice
. One consideration was to avoid unnecessary iterations over the wrapped iterator.
When calling itertools.islice(iterable, start, stop, step)
with stop<=start
, the result is an empty generator, as expected.
But even though it’s not absolutely needed, itertools.islice(iterable, start, stop, step)
will always iterate at least start
number of times over the iterable.
Test case to repro:
iterableMock = Mock()
iterableMock.__iter__ = Mock(return_value=iterableMock)
iterableMock.__next__ = Mock(side_effect=range(10))
iterable = iterableMock
start = 5
stop = 0
step = None
isliceResult = list(itertools.islice(iterable, start, stop, step))
assert isliceResult == []
assert iterableMock.__next__.call_count == 0 # <= FAILS since call_count == 5
Is this behavior:
- Expected / by design. We want to skip the elements until start is reached no matter what.
- Just a side effect of the current implementation that only impacts the performance of some corner cases.
- A potential improvement that can be addressed without concerns.
Documentation can be a bit misleading/misinterpreted on the expected behavior.
- elements from the iterable are skipped until start is reached
- it stops at the specified position
The suggested “equivalent implementation” and the source code clearly iterate until start-1
. stop
parameter is only considered subsequently.