0

Let's say I have a list like:

my_list = range(10)

And I want to count how many even numbers there are in the list. Note that I am not interested with the values, I just want the count of them. So I can:

len( [0 for i in my_list if i % 2 == 0] ) # Method 1
len( [i for i in my_list if i % 2 == 0] ) # Method 2
len( [_ for i in my_list if i % 2 == 0] ) # Method 3

Is any of the above methods better than others from the speed or memory perspectives?

Actually I don't even need to construct the list, but I don't want to:

counter = 0
for item in my_list:
   if item % 2 == 0:
      counter += 1

So, which one is a good way of counting with generators?

PS: The list in my case has more memory-heavy items, that is why I want to optimize if possible.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
Sait
  • 19,045
  • 18
  • 72
  • 99
  • 2
    recommended: learn how to use `timeit` and measure these results yourself. even easier if you use IPython and its builtin `%timeit` magic... – Corley Brigman Sep 15 '15 at 19:54
  • @CorleyBrigman Thank you for the recommendation. I use and love %timeit. However, the question here is mostly investigating the memory-efficiency. I was looking for another way to count items without generating the list itself. – Sait Sep 15 '15 at 19:59
  • that's true. also, maybe related: http://stackoverflow.com/questions/393053/length-of-generator-output ... btw, since you mentioned generators, you're working in python 3? – Corley Brigman Sep 15 '15 at 20:07
  • @CorleyBrigman nope. Python 2.7 4eva. – Sait Sep 15 '15 at 20:11

1 Answers1

8

Use none of the above. Use sum() and a generator expression:

sum(i % 2 == 0 for i in mylist)

In Python, the bool boolean type is a subclass of int and True has an integer value of 1, False has 0, so you can sum a series of True and False results.

The sum()-with-generator expression only has to keep one boolean in memory at a time, no intermediary list has to be produced and kept around just to calculate a length.

Alternatively, stick to filtering and sum 1 literals:

sum(1 for i in mylist if i % 2 == 0)

This results in fewer objects needing to be added up still.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • @TessellatingHeckler: Those [10 microseconds](http://stackoverflow.com/questions/1089936/is-faster-than-when-checking-for-odd-numbers) add up, don't they? – Martijn Pieters Sep 15 '15 at 19:49
  • @TessellatingHeckler Counting odd numbers was a show-case to explain my question. I am not actually interested in the fastest way to find out if a number is even or not. Thanks anyways. – Sait Sep 15 '15 at 19:51
  • 1
    @TessellatingHeckler: however, for `i = 2`, the expresion `~(i & 1)` produces -1, while for `i = 1` you get `-2`. That'll really mess with the outcomes here, as we are summing these -2 and -1, not 0 or 1. You'd have to use `1 - (i & 1)` instead, or divide the sum by -2.. – Martijn Pieters Sep 15 '15 at 19:52
  • @MartijnPieters eeeee, oops. back to the drawing-interactive-interpreter with me. – TessellatingHeckler Sep 15 '15 at 19:54
  • after reading another question, it looks like `sum(1 for i in mylist if i %2 == 0)` is a little faster (for an xrange(1000) in python 2.7, i get 112 uS for the sum of booleans, and 77uS for the sum of filtered 1s. – Corley Brigman Sep 15 '15 at 20:06