list comprehensions whats happening here

Steve Holden sholden at holdenweb.com
Fri Apr 13 15:19:21 EDT 2001


"Carlos Ribeiro" <cribeiro at mail.inet.com.br> wrote ...
> At 07:06 13/04/01 -0700, Emile van Sebille wrote:
> >I suspect the answer lies somewhere in the comma converting the parts of
the
> >list comprehension into a list itself, much as the comma is used in
normal
> >iteration idiom:
> >
> >for i in 1,2
>
> As other have pointed out, your're right, that does explain why it works
> that way. But...
>
>  >>>[[i,j] [1,2,3], for j in 'abc',]
> Traceback (  File "<interactive input>", line 1
>      [[i,j] [1,2,3], for j in 'abc',]
>
> does not work, yet
>
Why should this work? A list comprehension substitutes one or more bound
variables into a legitimate expression to generate the list members. But

    [i,j] [1,2,3]

is *not* a legitimate expression: the interpreter will complain because it
thinks you are trying to subscript the list [i,j] with a tuple 1,2,3 -- list
subscripts have to be integers.

>  >>> [[i,j] for i in range(3), for j in range(3)]
> [[[0, 1, 2], 0], [[0, 1, 2], 1], [[0, 1, 2], 2]]
>
> work.
>
Indeed. Let's take this a piece at a time... start by giving j a fixed value
42.

    [[i,j] for i in range(3),]

is a legitimate expression: it is equivalent to

    [[i,j] for i in (range(3), )]

In other words, it produces a list of [i,j] lists, for i taking on every
value of the tuple (range(3), ) - a single-element tuple, so the result from
either of these expressions would be

    [[[0, 1, 2], 42]]

This is correct: we get a single element list whose only element is the list
[i, j], where i was range(3) (the only value in the single-element tuple i
was iterating across)  and j was 42.

Let's look at your expression again, using a different range for j just so
it's clearer which contributes to what:

    [[i,j] for i in range(3), for j in range(6,9)]

should give you a list containing three elements, since now j takes on not
just a single value but three. Sure enough, the result is

    [[[0, 1, 2], 6], [[0, 1, 2], 7], [[0, 1, 2], 8]]

This is a list of three elements, one for each value of j. Each element of
the list is a list of two elements, the first of which is the value of i
(which is to say, range(3), or [0,1,2]) followed by a value of j (which is
to say 6, then 7, then 8).

And that's why

[[i,j] for i in range(3), for j in range(3)]

evaluates to

[[[0, 1, 2], 0], [[0, 1, 2], 1], [[0, 1, 2], 2]]

It's also why

[[i,j] for i in range(3), for j in range(6,9),] # note second trailing comma

has only one value - because both i and j are iterating over single-element
tuples.

[[[0, 1, 2], [6, 7, 8]]]


> I am not advocating that something is wrong; I just want to point out that
> some funny things are happening as a side effect of the grammar and/or
> compiler. It's impossible to supply a list as part of the list
> comprehension syntax, but you can fake it by writing a comma after the for
> clause. That's just weird, and unexpected.
>
Nope, that's a tuple, not a list. Just so happened that it's a
single-element tuple, whose only member is a list. If it's any help, I agree
it's weird and unexpected, but you have to realise we're out on the edges
here.

> Question: this may turn out to be wrong, and it may be fixed in the
future;
> OTOH, it can be useful, and (in a rather obscure way) consistent with
> Python syntax. What's the take on this? Let it be, or fix it, as a
> undesirable side effect that don't belong to list comprehensions?
>
My own take is that everything is entirely consistent, and should be left
alone. There's a good reason why the list comprehension syntax doesn't have
commas in it!

Hope this helps.

regards
 Steve






More information about the Python-list mailing list