views:

521

answers:

5

I see this kind of thing sometimes:

(k for k in (j for j in (i for i in xrange(10))))

Now this really bends my brain, and I would rather it wasn't presented in this way.

Are there any use-cases, or examples of having used these nested expressions where it was more elegant and more readable than if it had been a nested loop?

Edit: Thanks for the examples of ways to simplify this. It's not actually what I asked for, I was wondering if there were any times when it was elegant.

+1  A: 

Since they are generator expressions, you can bind each to it's own name to make it more readable without any change in performance. Changing it to a nested loop would likely be detrimental to performance.

irange = (i for i in xrange(10))
jrange = (j for j in irange)
krange = (k for k in jrange)

It really doesn't matter which you choose, I think the multi-line example is more readable, in general.

Jeremy Cantrell
A: 

In the case of your example, I would probably write it as:

foos = (i for i in xrange(10))
bars = (j for j in foos)
bazs = (k for k in bars)

Given more descriptive names, I think this would probably be quite clear, and I can't imagine there being any measurable performance difference.

Perhaps you're thinking more of expressions like:

(x for x in xs for xs in ys for ys in lst)

-- actually, that's not even valid. You have to put things in the other order:

(x for ys in lst for xs in ys for x in xs)

I might write that as a quick way of flattening a list, but in general I think you're write: the time you save by typing less is usually balanced by the extra time you spend getting the generator expression right.

John Fouhy
+4  A: 

If you're worried about too much complexity on one line, you could split it:

(k for k in 
    (j for j in 
        (i for i in xrange(10))))

I've always found line continuations to look a little weird in Python, but this does make it easier to see what each one is looping over. Since an extra assignment/lookup is not going to make or break anything, you could also write it like this:

gen1 = (i for i in xrange(10))
gen2 = (j for j in gen1)
gen3 = (k for k in gen2)

In practice, I don't think I've ever nested a comprehension more than 2-deep, and at that point it was still pretty easy to understand.

DNS
I removed the escapes.
Soviut
+4  A: 

Check PEP 202 which was where list comprehensions syntax was introduced to the language.

For understanding your example, there is a simple rule from Guido himself:

  • The form [... for x... for y...] nests, with the last index varying fastest, just like nested for loops.

Also from PEP 202, which serves to answer your question:

Rationale
    List comprehensions provide a more concise way to create lists in
    situations where map() and filter() and/or nested loops would
    currently be used.

If you had a situation like that, you could find it to be more elegant. IMHO, though, multiple nested list comprehensions may be less clear in your code than nested for loops, since for loops are easily parsed visually.

popcnt
+1  A: 

The expression:

(k for k in (j for j in (i for i in xrange(10))))

is equivalent to:

(i for i in xrange(10))

that is almost the same:

xrange(10)

The last variant is more elegant than the first one.

J.F. Sebastian