-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathwindowed_iterator.py
83 lines (73 loc) · 2.87 KB
/
windowed_iterator.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
from collections import deque
import itertools
class windowed_iterator:
""" Wrap an iterator s.t. we can see [window] neighbors
in either direction from the current item.
Items are stored in a deque of size 2*window + 1, where the latest
item is always in the middle position.
The supplied clear_callback() is called for items more than
[window] steps in the past.
"""
# Todo? remove use of None as sentinel, to be able to represent
# iterators returning None.
def __init__(self, iterator, window, clear_callback=None):
self.iterator = iterator
# initialize deque with sentinel values
self.items = deque((None for i in range(window + 1)),
window * 2 + 1)
self.window = window
self.clear_callback = clear_callback
def __iter__(self):
return self
def __repr__(self):
return str(self.items) + ' window: ' + str(self.window)
def clear(self):
for item in self.items:
if item and self.clear_callback is not None:
self.clear_callback(item)
self.items.clear()
def neighbor(self, delta):
if abs(delta) > self.window:
raise IndexError('Requested delta outside window')
while self.window + delta + 1 > len(self.items):
try:
self.items.append(self.iterator.next())
except StopIteration:
return None
return self.items[self.window + delta]
def neighbors(self, window=None, modtwo=False):
if window is None:
window = self.window
if window > self.window:
raise IndexError('Requested delta outside window')
for i in itertools.chain(range(-window, 0),
range(1, window + 1)):
if modtwo and i % 2 == 1:
continue
n = self.neighbor(i)
if n is not None:
yield n
def next(self):
nextitem = None
if len(self.items) == self.window + 1:
# elicit potential StopIteration before clearing/popping
nextitem = self.iterator.next()
if self.items[0] is not None and self.clear_callback is not None:
self.clear_callback(self.items[0])
self.items.popleft()
if nextitem is not None:
self.items.append(nextitem)
return self.items[self.window]
if __name__ == '__main__':
def sample_gen():
for i in range(0, 10):
yield i, { 'squared': i*i }
g = sample_gen()
c = windowed_iterator(g, 3)
for i, item in enumerate(c):
print 'item %s: %s' % (i, item)
# print c
if i in (1, 4, 6, 9):
print 'neighbors of item %s: %s' % (i, [n for n in c.neighbors(2)])
if i in (4, 9):
print '2-neighbors of item %s: %s' % (i, [n for n in c.neighbors(2, modtwo=True)])