Joan Venge
Joan Venge

Reputation: 331460

Multiple statements in list compherensions in Python?

Is it possible to have something like:

list1 = ...

currentValue = 0
list2 = [currentValue += i, i for i in list1]

I tried that but didn't work? What's the proper syntax to write those?

EDIT: the print statement was an example. Actually I am incrementing a value outside the loop.

Upvotes: 17

Views: 28344

Answers (10)

qwr
qwr

Reputation: 11024

Having that makes list comprehensions much harder to understand. List comprehensions usually apply a function to each element of a list and don't keep track of mutable state.

[f(x) for x in l]  
list(map(f, l))  # similar

You can of course apply a function that returns multiple values:

[(x, x+1) for x in l]  # a little shorter
list(map(lambda x: (x, x+1), l))

Upvotes: 0

Manu
Manu

Reputation: 191

As pjz said, you can use functions, so here you can use a closure to keep track of the counter value:

# defines a closure to enclose the sum variable
def make_counter(init_value=0):
    sum = [init_value]
    def inc(x=0):
        sum[0] += x
        return sum[0]
    return inc

Then you do what you want with list1:

list1 = range(5)  # list1 = [0, 1, 2, 3, 4]

And now with just two lines, we get list2:

counter = make_counter(10)  # counter with initial value of 10
list2 = reduce(operator.add, ([counter(x), x] for x in list1))

In the end, list2 contains:

[10, 0, 11, 1, 13, 2, 16, 3, 20, 4]

which is what you wanted, and you can get the value of the counter after the loop with one call:

counter()  # value is 20

Finally, you can replace the closure stuff by any kind of operation you want, here we have an increment, but it's really up to you. Note also that we use a reduce to flatten list2, and this little trick requires you to import operator before calling the line with the reduce:

import operator

Upvotes: 3

Matt
Matt

Reputation: 1901

Why would you create a duplicate list. It seems like all that list comprehension would do is just sum the contents.

Why not just.

list2 = list(list1)   #this makes a copy
currentValue = sum(list2)

Upvotes: 1

John Fouhy
John Fouhy

Reputation: 42233

For your edited example:

currentValue += sum(list1)

or:

for x in list1:
    currentValue += x

List comprehensions are great, I love them, but there's nothing wrong with the humble for loop and you shouldn't be afraid to use it :-)

EDIT: "But what if I wanna increment different than the collected values?"

Well, what do you want to increment by? You have the entire power of python at your command!

Increment by x-squared?

for x in list1:
    currentValue += x**2

Increment by some function of x and its position in the list?

for i, x in enumerate(list1):
    currentValue += i*x

Upvotes: 1

Brandon Rhodes
Brandon Rhodes

Reputation: 89565

Statements cannot go inside of expressions in Python; it was a complication that was deliberately designed out of the language. For this problem, try using a complication that did make it into the language: generators. Watch:

def total_and_item(sequence):
    total = 0
    for i in sequence:
        total += i
        yield (total, i)

list2 = list(total_and_item(list1))

The generator keeps a running tally of the items seen so far, and prefixes it to each item, just like it looks like you example tries to do. Of course, a straightforward loop might be even simpler, that creates an empty list at the top and just calls append() a lot! :-)

Upvotes: 33

pjz
pjz

Reputation: 43117

You can't do multiple statements, but you can do a function call. To do what you seem to want above, you could do:

list1 = ...
list2 = [ (sum(list1[:i], i) for i in list1 ]

in general, since list comprehensions are part of the 'functional' part of python, you're restricted to... functions. Other folks have suggested that you could write your own functions if necessary, and that's also a valid suggestion.

Upvotes: 0

ojrac
ojrac

Reputation: 13421

Print is a weird thing to call in a list comprehension. It'd help if you showed us what output you want, not just the code that doesn't work.

Here are two guesses for you. Either way, the important point is that the value statement in a list comprehension has to be a single value. You can't insert multiple items all at once. (If that's what you're trying to do, skip to the 2nd example.)

list1 = [1, 2, 3]
list2 = [(i, i*2, i) for i in list1]
# list2 = [(1, 2, 1), (2, 4, 2), (3, 6, 3)]

To get a flat list:

list1 = [1, 2, 3]
tmp = [(i, i*2) for i in list1]
list2 = []
map(list2.extend, tmp)
# list2 = [1, 2, 1, 2, 4, 2, 3, 6, 3]

Edit: Incrementing a value in the middle of the list comprehension is still weird. If you really need to do it, you're better off just writing a regular for loop, and appending values as you go. In Python, cleverness like that is almost always branded as "unpythonic." Do it if you must, but you will get no end of flak in forums like this. ;)

Upvotes: 1

Charlie Martin
Charlie Martin

Reputation: 112414

Here's an example from another question:

[i for i,x in enumerate(testlist) if x == 1]

the enumerate generator returns a 2-tuple which goes into i,x.

Upvotes: 2

David Z
David Z

Reputation: 131750

I'm not quite sure what you're trying to do but it's probably something like

list2 = [(i, i*2, i) for i in list1]
print list2

The statement in the list comprehension has to be a single statement, but you could always make it a function call:

def foo(i):
    print i
    print i * 2
    return i
list2 = [foo(i) for i in list1]

Upvotes: 6

phihag
phihag

Reputation: 288270

First of all, you likely don't want to use print. It doesn't return anything, so use a conventional for loop if you just want to print out stuff. What you are looking for is:

>>> list1 = (1,2,3,4)
>>> list2 = [(i, i*2) for i in list1] # Notice the braces around both items
>>> print(list2)
[(1, 2), (2, 4), (3, 6), (4, 8)]

Upvotes: 1

Related Questions