Welcome, guest | Sign In | My Account | Store | Cart

Implementation of isplit, a function that splits iterators into two equal ones, which return similar values, but are exact copies of one another.

Python, 87 lines
 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
84
85
86
87
import copy

class _IDup(object):
        """Internal class used only to keep a reference on the actual iterator,
        and to do housekeeping."""

        def __init__(self,iterin):
                self.__iter = iterin
                self.__iterno = 0
                self.__iteritems = []
                self.__hasstopped = None

        def registerIter(self,oldno=-1):
                iterno = self.__iterno
                self.__iterno += 1
                if oldno == -1:
                        self.__iteritems.append([])
                else:
                        self.__iteritems.append(
                                copy.deepcopy(self.__iteritems[oldno])
                                )
                return iterno

        def getNext(self,iterno):
                if self.__iteritems[iterno]:
                        iteritem = self.__iteritems[iterno].pop(0)
                elif self.__hasstopped is not None:
                        raise self.__hasstopped
                else:
                        try:
                                iteritem = self.__iter.next()
                        except StopIteration, e:
                                self.__hasstopped = e
                                raise
                        for id, i in enumerate(self.__iteritems):
                                if id <> iterno:
                                        i.append(copy.deepcopy(iteritem))
                return iteritem

class _IDupped(object):
        """Duplicated Iterator class. Each iterator you get by calling isplit
        or split on a splitted iterator will be of this type."""

        def __init__(self,idup,oldno=-1):
                self.__idup = idup
                self.__iterno = idup.registerIter(oldno)

        def next(self):
                return self.__idup.getNext(self.__iterno)

        def split(self):
                """Split this iterator into two pieces. The original iterator
                is still callable, as is the sub-iterator."""

                return _IDupped(self.__idup,self.__iterno)

        def __iter__(self):
                return self

def isplit(iterin,splitno=2):
        idup = _IDup(iterin)
        iduppeds = []
        for i in range(splitno):
                iduppeds.append(_IDupped(idup))
        return tuple(iduppeds)

# Create first few iterators.
test = ["hello","how","are","you?"]
x, y = isplit(iter(test))

# Test print of iterator y.
print "First item of y."
print y.next()

# Create new iterator z after first element of y.
z = y.split()

# Print rest of the elements.
print "Rest in x."
for i in x:
        print i
print "Rest in y."
for i in y:
        print i
print "Rest in z."
for i in z:
        print i

The problem of having to split iterators occured to me when I had one iterator, which kept returning lines from a source-file, which I wanted two different functions to process simultaneously. These functions ran in the same context, but I thought it was cleaner passing each function its own iterator versus calling both functions from a loop which read the lines one by one from the file.

2 comments

Raymond Hettinger 20 years, 8 months ago  # | flag

Here's a simpler and faster implementation.

import itertools

def isplit(iterable):
    isplit
.cnt = 0
    data
= {}
    it
= iter(iterable)
   
def f(it):
       
next = it.next
       
for i in itertools.count():
           
if i == isplit.cnt:
                item
= data[i] = next()
                isplit
.cnt += 1
           
else:
                item
= data.pop(i)
           
yield item
   
return f(it), f(it)


test
= ["hello","how","are","you?"]
x
, y = isplit(test)
print list(x), list(y)
Raymond Hettinger 20 years, 7 months ago  # | flag

Version that can return more than two iterators.

def multi_iter(iterable, n=2):
   
"Return multiple iterators (default is 2) from a single iterable"

   
def f(next, data, n, cnt):
        i
= 0
       
while 1:
           
if i == cnt[0]:
                item
= next()
                cnt
[0] += 1
                data
[i] = [item, n-1]
           
else:
                item
, refcnt = entry = data[i]
               
if refcnt == 1:
                   
del data[i]
               
else:
                    entry
[1] = refcnt - 1
           
yield item
            i
+= 1
    data
, cnt, next = {}, [0], iter(iterable).next
   
return [f(next, data, n, cnt) for j in range(n)]

# Example
w
, x, y, z = multi_iter("shrubbery", 4)
print zip(w,x), list(y), tuple(z)
Created by Heiko Wundram on Tue, 29 Jul 2003 (PSF)
Python recipes (4591)
Heiko Wundram's recipes (1)

Required Modules

Other Information and Tasks