Multiple statements in list compherensions in Pyth

2019-03-25 13:52发布

问题:

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.

回答1:

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! :-)



回答2:

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]


回答3:

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.



回答4:

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


回答5:

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)]


回答6:

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. ;)



回答7:

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


回答8:

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)


回答9:

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.