views:

224

answers:

5

I want to do something like:

all = [ x for x in t[1] for t in tests ]

tests looks like:

[ ("foo",[a,b,c]), ("bar",[d,e,f]) ]

So I want to have the result

all = [a,b,c,d,e,f]

My code does not work, Python says:

UnboundLocalError: local variable 't' referenced before assignment

Is there any simple way to do that?

+5  A: 

It should work the other way around:

all = [x for t in tests for x in t[1]]
Ferdinand Beyer
Thanks, I wonder why I haven't tested that myself. :) Have tried so many different ways.
Albert
Nice, I finally understood what was this question about :-)
Kugel
I think Python's choice of order for multiple-`for` list comprehensions was a mistake. I understand it's trying to replicate the use of nested for statements but I find the results unreadable and tend to avoid it.
bobince
@bobince +1 totally agreed, if the intent was to keep it coherent with the normal `for`, a better way would have been just to surround the block with brackets like `[for x in l: f(x)]`; but in this case keeping the usage and the definition of the loop variables closer makes more sense.
fortran
A: 

what about: all = [ y for (x, y) in tests ]? What is your expected result?

RC
Well, you still would have to use nested `for`s: `[item for name, items in tests for item in items]`.
Ferdinand Beyer
+2  A: 

When in doubt, don't use list comprehensions.

Try import this in your Python shell and read the second line:

Explicit is better than implicit

This type of compounding of list comprehensions will puzzle a lot of Python programmers so at least add a comment to explain that you are removing strings and flattening the remaining list.

Do use list comprehensions where they are clear and easy to understand, and especially, do use them when they are idiomatic, i.e. commonly used because they are the most efficient or elegant way to express something. For instance, this Python Idioms article gives the following example:

result = [3*d.Count for d in data if d.Count > 4]

It is clear, simple and straightforward. Nesting list comprehensions is not too bad if you pay attention to formatting, and perhaps add a comment because the braces help the reader to decompose the expression. But the solution that was accepted for this problem is too complex and confusing in my opinion. It oversteps the bounds and makes the code unreadable for too many people. It is better to unroll at least one iteration into a for loop.

Michael Dillon
I hope I understand you wrong: *don't use list comprehensions*. **Do** use list comprehensions, normal operations (like map) are very clear that way (`[f(x) for x in items]`).
kaizer.se
I greatly prefer `map(f, items)` to `[f(x) for x in items]`. It's like a lot of other Python idioms - e.g. `dict(zip(keys, values))` - in that once you've internalized it, using it makes code more readable by eliminating unnecessary entities.
Robert Rossney
+1  A: 

That looks like a reduce to me. Unfortunately Python does not offer any syntactic sugar for reduce, so we have to use lambda:

reduce(lambda x, y: x+y[1], tests, [])
+2  A: 

If all you are doing is adding together some lists, try the sum builtin, using [] as a starting value:

all = sum((t[1] for t in tests), [])
Paul McGuire