Because the value of i is not evaluated until the generator is
actually run; so all the generators end up seeing only the final value
of i rather than the intended values. This is a common problem with
generator expressions that are not immediately run.
> I tried changing the t 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 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?

That avoids the generator problem, but in this case you could get the
same result a bit more straight-forwardly by just using imap instead:

Internally, the tee's iterators are going to accumulate a ton of data
unless they are consumed roughly in parallel. Of course, if they are
consumed *exactly* in lockstep, the you don't need to split them into
separate iterables -- just use the tuples as they come.

In fact, split(x) is the same as zip(*x) when x is finite. The
difference is that with split(x), x is allowed to be infinite and with
zip(*x), each term of x is allowed to be infinite. It may be good to
have a function unifying the two.

Share This Page

Welcome to The Coding Forums!

Welcome to the Coding Forums, the place to chat about anything related to programming and coding languages.

Please join our friendly community by clicking the button below - it only takes a few seconds and is totally free. You'll be able to ask questions about coding or chat with the community and help others.
Sign up now!