Skip to content

Incorrect and overly-complicated islice example implementation in docs #118476

@FHTMitchell

Description

@FHTMitchell

Documentation

The itertools.islice reference implementation in the docs is complex and also slightly incorrect. For reference it is here

def islice(iterable, *args):
    s = slice(*args)
    start, stop, step = s.start or 0, s.stop or sys.maxsize, s.step or 1
    it = iter(range(start, stop, step))
    try:
        nexti = next(it)
    except StopIteration:
        # Consume *iterable* up to the *start* position.
        for i, element in zip(range(start), iterable):
            pass
        return
    try:
        for i, element in enumerate(iterable):
            if i == nexti:
                yield element
                nexti = next(it)
    except StopIteration:
        # Consume to *stop*.
        for i, element in zip(range(i + 1, stop), iterable):
            pass

This does not correctly handle stop == 0, overwriting it to be sys.maxsize and also assumes its input is an iterator, not any iterable.

It is also just hard to reason about. Instead I propose

def islice(iterable, *args):
      s = slice(*args)
      start = s.start if s.start is not None else 0
      stop = s.stop
      step = s.step if s.step is not None else 1
      it = iter(iterable)
      for _ in zip(range(start), it):
          # Consume up to *start* position
          pass
      if stop is not None and stop <= start:
          return
      for i, element in enumerate(it, start):
          if (i - start) % step == 0:
              yield element
          if stop is not None and i + 1 >= stop:
              return

This matches islice exactly for these inputs

for start in itertools.chain(range(6), [None]):
    for stop in itertools.chain(range(6), [None]):
        for step in itertools.chain(range(1, 6), [None]):
            for r in [range(0), range(4)]:
                it = iter(r)
                myx = list(islice(it, start, stop, step))
                myy = list(it)
                it = iter(r)
                pyx = list(itertools.islice(it, start, stop, step))
                pyy = list(it)
                msg = "ERR" if myx != pyx or myy != pyy else "ok"
                print(
                    f"{msg}: {r=}, {start=}, {stop=}, {step=}, {myx=}, {pyx=}, {myy=}, {pyy=}"
                )

Linked PRs

Metadata

Metadata

Assignees

Labels

docsDocumentation in the Doc dir

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions