I came across a bit of code in StackOverflow that raised two questions about the way deque
works. I don't have enough reputation to ask "in situ", therefore this question:
from collections import deque
from itertools import islice
def sliding_window(iterable, size=2, step=1, fillvalue=None):
if size < 0 or step < 1:
raise ValueError
it = iter(iterable)
q = deque(islice(it, size), maxlen=size)
if not q:
return # empty iterable or size == 0
q.extend(fillvalue for _ in range(size - len(q))) # pad to size
while True:
yield iter(q) # iter() to avoid accidental outside modifications
q.append(next(it))
q.extend(next(it, fillvalue) for _ in range(step - 1))
The code computes a sliding window of a given size over a sequence. The steps I don't understand are first:
q = deque(islice(it, size), maxlen=size)
What is the use of maxlen
here? Isn't islice
always going to output an iterable of at most length size
?
And second:
yield iter(q) # iter() to avoid accidental outside modifications
why do we need to transform to to iterable to avoid "accidental outside modifications"?
To answer second part of the question, everything in Python is passed by reference. So in case of above generator q
is a reference to the original deque hold by the function, so any method that may amend the deque, would break original algorithm of the generation. When you surround q
with iter()
what you effectively have yielded is an iterator. You can take elements from iterator (read), but you cannot change elements itself or amend the sequence of them (write not allowed). So it's a good practice to protect from accidental damage to the container hold internally be the generator.