can't generate iterator from list

Peter Otten __peter__ at web.de
Sat Sep 10 09:05:43 CEST 2011


Dr. Phillip M. Feldman wrote:

> 
> It is supposed to be possible to generate a list representation of any
> iterator that produces a sequence of finite length, but this doesn't
> always work. Here's a case where it does work:
> 
> Input:
> 
> from itertools import combinations
> list(combinations(range(4),2))
> 
> Output:
> 
> [(0, 1), (0, 2), (0, 3), (1, 2), (1, 3), (2, 3)]
> 
> When I define my own classes that produce iterators, conversion to a list
> works for some of these classes but not for others. Here's a case where it
> doesn't work:
> 
> In:  list(balls_in_numbered_boxes(2, [3,3,3]))
> 
> Out:
> 
> [array([0, 0, 1]),
>  array([0, 0, 1]),
>  array([0, 0, 1]),
>  array([0, 0, 1]),
>  array([0, 0, 1])]

[snip code where excessive commenting does more bad than good]

The problem is that you return the same mutable object on every next() call. 
Here's a simplified example:

>>> def g(items):
...     x = [None]
...     for item in items:
...             x[0] = item
...             yield x
...
>>> list(g("abc"))
[['c'], ['c'], ['c']]

When you invoke it using next() you are fooled into thinking that it works 
as desired:

>>> it = g("abc")
>>> a = next(it)
>>> a
['a']
>>> b = next(it)
>>> b
['b']

but only until you look back at the previous item:

>>> a
['b']

Once you understand what is going on the fix is easy -- don't reuse the 
mutable object:

>>> def g(items):
...     for item in items:
...             yield [item]
...
>>> list(g("abc"))
[['a'], ['b'], ['c']]





More information about the Python-list mailing list