Steven D'Aprano wrote: > Suppose I have an iterator that yields tuples of N items (a, b, ... n). > > I want to split this into N independent iterators: > > iter1 -> a, a2, a3, ... > iter2 -> b, b2, b3, ... > ... > iterN -> n, n2, n3, ... > > The iterator may be infinite, or at least too big to collect in a list. > > My first attempt was this: > > > def split(iterable, n): > iterators = [] > for i, iterator in enumerate(itertools.tee(iterable, n)): > iterators.append((t[i] for t in iterator)) > return tuple(iterators) > > But it doesn't work, as all the iterators see the same values: > >>>> data = [(1,2,3), (4,5,6), (7,8,9)] >>>> a, b, c = split(data, 3) >>>> list(a), list(b), list(c) > ([3, 6, 9], [3, 6, 9], [3, 6, 9]) > > > I tried changing the t[i] to use operator.itergetter instead, but no > luck. Finally I got this: > > def split(iterable, n): > iterators = [] > for i, iterator in enumerate(itertools.tee(iterable, n)): > f = lambda it, i=i: (t[i] for t in it) > iterators.append(f(iterator)) > return tuple(iterators) > > which seems to work: > >>>> data = [(1,2,3), (4,5,6), (7,8,9)] >>>> a, b, c = split(data, 3) >>>> list(a), list(b), list(c) > ([1, 4, 7], [2, 5, 8], [3, 6, 9]) > > > > > Is this the right approach, or have I missed something obvious?
Here's how to do it with operator.itemgetter(): >>> from itertools import * >>> from operator import itemgetter >>> data = [(1,2,3), (4,5,6), (7,8,9)] >>> abc = [imap(itemgetter(i), t) for i, t in enumerate(tee(data, 3))] >>> map(list, abc) [[1, 4, 7], [2, 5, 8], [3, 6, 9]] I'd say the improvement is marginal. If you want to go fancy you can calculate n: >>> def split(items, n=None): ... if n is None: ... items = iter(items) ... first = next(items) ... n = len(first) ... items = chain((first,), items) ... return [imap(itemgetter(i), t) for i, t in enumerate(tee(items, n))] ... >>> map(list, split([(1,2,3), (4,5,6), (7,8,9)])) [[1, 4, 7], [2, 5, 8], [3, 6, 9]] Peter -- http://mail.python.org/mailman/listinfo/python-list