I'm creating a small library of Python utilities, and I'd like feedback on a function which allows iterating over an arbitrary iterable in a sliding-window fashion.
Relevant parts of iteration.py
:
import collections
import itertools
def sliding_window_iter(iterable, size):
"""Iterate through iterable using a sliding window of several elements.
Creates an iterable where each element is a tuple of `size`
consecutive elements from `iterable`, advancing by 1 element each
time. For example:
>>> list(sliding_window_iter([1, 2, 3, 4], 2))
[(1, 2), (2, 3), (3, 4)]
"""
iterable = iter(iterable)
window = collections.deque(
itertools.islice(iterable, size-1),
maxlen=size
)
for item in iterable:
window.append(item)
yield tuple(window)
Relevant parts of the test file iteration_test.py
:
import doctest
import unittest
import iteration
from iteration import *
class TestSlidingWindowIter(unittest.TestCase):
def test_good(self):
self.assertSequenceEqual(
list(sliding_window_iter([1, 2, 3, 4], 2)),
[(1, 2), (2, 3), (3, 4)]
)
def test_exact_length(self):
self.assertSequenceEqual(
list(sliding_window_iter(["c", "b", "a"], 3)),
[("c", "b", "a")]
)
def test_short(self):
self.assertSequenceEqual(
list(sliding_window_iter([1, 2], 3)),
[]
)
def test_size_one(self):
self.assertSequenceEqual(
list(sliding_window_iter([1, 2, 3, 4], 1)),
[(1,), (2,), (3,), (4,)]
)
def test_bad_size(self):
with self.assertRaises(ValueError):
list(sliding_window_iter([1, 2], 0))
def run():
if not doctest.testmod(iteration)[0]:
print("doctest: OK")
unittest.main()
if __name__ == "__main__":
run()
I'm primarily looking for feedback in these areas:
- Is the code Pythonic? I'm primarily a C++ developer, so Python idioms don't come naturally to me; that's one thing I'm constantly trying to improve.
- Are there any potential performance problems?
- Am I reinventing a wheel and something like this already exists?
I'll welcome any other feedback too, of course.