# inverse of izip

Peter Otten __peter__ at web.de
Thu Aug 19 20:55:45 CEST 2004

```Steven Bethard wrote:

> Peter Otten <__peter__ at web.de> wrote in message
> news:<cg1pct\$p58\$00\$1 at news.t-online.com>...
>> repeating your demo, you are getting the same (last) sequence twice due
>> to late binding of i.
>>
> [snip]
>> >>> map(list, starzip(it.izip("123", "abc")))
>>  [['1', '2', '3'], ['a', 'b', 'c']]
>> >>> x, y = starzip(it.izip("123", "abc"))
>> >>> list(x)
>>  ['a', 'b', 'c']
>> >>> list(y)
>>  ['a', 'b', 'c']
>> >>>
>
> I knew there was something funny about binding in generators, but I
> couldn't remember what...  Could you explain why 'map(list, ...)'
> works, but 'x, y = ...' doesn't?  I read the PEP, but I'm still not
> clear on this point.

Maybe the following example can illustrate what I think is going on:

import itertools as it

def starzip(iterables):
return ((t[i] for t in itr)
for (i, itr) in
enumerate(it.tee(iterables)))

# the order of calls equivalent to map(list, starzip(...))
s = starzip(it.izip("abc", "123"))
x = s.next()
# the local variable i in starzip() shared by x and y
# is now 0
print x.next(),
print x.next(),
print x.next()
y = s.next()
# i is now 1, but because no further calls to x.next()
# will occur it doesn't matter
print y.next(),
print y.next(),
print y.next()

s = starzip(it.izip("abc", "123"))
x = s.next() # i is 0
y = s.next() # i is 1
# both x an y yield t[1]
print x.next(),
print x.next(),
print x.next()

print y.next(),
print y.next(),
print y.next()

You can model the nested generator expressions' behaviour with the following
function - which I think is much clearer.

def starzip(iterables):
def inner(itr):
for t in itr:
yield t[i]

for (i, itr) in enumerate(it.tee(iterables)):
yield inner(itr)

Note how itr is passed explicitly, i. e. it is not affected by later
rebindings in startzip() whereas i is looked up in inner()'s surrounding
namespace at every yield.

Peter

```