3

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
Claudiordgz
  • 3,023
  • 1
  • 21
  • 48

3 Answers3

9

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.

kindall
  • 178,883
  • 35
  • 278
  • 309
7

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

rlms
  • 10,650
  • 8
  • 44
  • 61
  • I can't since in my program foo is a container object, the append method does some other things. – Claudiordgz Jul 30 '13 at 20:58
  • 1
    Then either just use a for loop, or, if the container object is of a class you defined, maybe change the `__init__` to use *args so you can do `foo = MyContainerFoo(sum(i,x) for i, x in enumerate(bar_list))` – rlms Jul 30 '13 at 21:00
  • That doesn't require the use of `*args`. The generator expression is one argument. – kindall Jul 30 '13 at 21:01
  • Sorry, I meant I would prefer to define it so it can be initialized as either `MyClass(1,2,3,4,5)` (using *args) or `MyClass(*(i for i in range(1,6)))`, rather than `MyClass([1,2,3,4,5])` or `MyClass(i for i in range(1,6)`, but thats just me. – rlms Jul 31 '13 at 07:34
3

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)]
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343