Question

I created a line that appends an object to a list in the following manner

>>> foo = list()
>>> def sum(a, b):
...      c = a+b; return c
...
>>> bar_list = [9,8,7,6,5,4,3,2,1,0]
>>> [foo.append(sum(i,x)) for i, x in enumerate(bar_list)]
[None, None, None, None, None, None, None, None, None, None]
>>> foo
[9, 9, 9, 9, 9, 9, 9, 9, 9, 9]
>>>

The line

[foo.append(sum(i,x)) for i, x in enumerate(bar_list)]

would give a pylint W1060 Expression is assigned to nothing, but since I am already using the foo list to append the values I don't need to assing the List Comprehension line to something.

My questions is more of a matter of programming correctness

Should I drop list comprehension and just use a simple for expression?

>>> for i, x in enumerate(bar_list):
...      foo.append(sum(i,x))

or is there a correct way to use both list comprehension an assign to nothing?

Answer

Thank you @user2387370, @kindall and @Martijn Pieters. For the rest of the comments I use append because I'm not using a list(), I'm not using i+x because this is just a simplified example.

I left it as the following:

histogramsCtr = hist_impl.HistogramsContainer()
for index, tupl in enumerate(local_ranges_per_histogram_list):
    histogramsCtr.append(doSubHistogramData(index, tupl))
return histogramsCtr
Was it helpful?

Solution

Yes, this is bad style. A list comprehension is to build a list. You're building a list full of Nones and then throwing it away. Your actual desired result is a side effect of this effort.

Why not define foo using the list comprehension in the first place?

foo = [sum(i,x) for i, x in enumerate(bar_list)]

If it is not to be a list but some other container class, as you mentioned in a comment on another answer, write that class to accept an iterable in its constructor (or, if it's not your code, subclass it to do so), then pass it a generator expression:

foo = MyContainer(sum(i, x) for i, x in enumerate(bar_list))

If foo already has some value and you wish to append new items:

foo.extend(sum(i,x) for i, x in enumerate(bar_list))

If you really want to use append() and don't want to use a for loop for some reason then you can use this construction; the generator expression will at least avoid wasting memory and CPU cycles on a list you don't want:

any(foo.append(sum(i, x)) for i, x in enumerate(bar_list))

But this is a good deal less clear than a regular for loop, and there's still some extra work being done: any is testing the return value of foo.append() on each iteration. You can write a function to consume the iterator and eliminate that check; the fastest way uses a zero-length collections.deque:

from collections import deque
do = deque([], maxlen=0).extend

do(foo.append(sum(i, x)) for i, x in enumerate(bar_list))

This is actually fairly readable, but I believe it's not actually any faster than any() and requires an extra import. However, either do() or any() is a little faster than a for loop, if that is a concern.

OTHER TIPS

I think it's generally frowned upon to use list comprehensions just for side-effects, so I would say a for loop is better in this case.

But in any case, couldn't you just do foo = [sum(i,x) for i, x in enumerate(bar_list)]?

You should definitely drop the list comprehension. End of.

  • You are confusing anyone reading your code. You are building a list for the side-effects.
  • You are paying CPU cycles and memory for building a list you are discarding again.

In your simplified case, you are overlooking the fact you could have used a list comprehension directly:

[sum(i,x) for i, x in enumerate(bar_list)]
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top