When to use list comprehension and not filter+lambda?

Asked

Viewed 878 times

8

Good night, you guys. Studying here, I found the functions of List Comprehension and filter+lambda, but both seemed very similar to me on some points...

When should I wear List Comprehension instead of filter+lambda, and vice versa ?

2 answers

5

I would not say that one is "better" or "worse" than the other, in reality both in performance and in concision the two forms are quite similar. The only thing that changes is the style programming.

Imperative style (loop):

impares = []
for i in range(1000):
    if i % 2:
        impares.append(i)

Functional style (filter):

impares = filter(lambda i: i % 2, range(1000))       # Python 2
impares = list(filter(lambda i: i % 2, range(1000))) # Python 3

Declarative style (list comprehension):

impares = [i for i in range(1000) if i % 2]

All three forms are readable and must have similar performance. The imperative style is less concise, so I would only use it when the test expression is more complex (which would make a single lambda become too long, requiring an auxiliary function to remain readable).

Personally, I consider the comprehension of lists more "light on the eye" so that I never write code using filter (chiefly filter + lambda).

On questions of efficiency pointed out by sergiopereira, in fact the filter and list understandings create a new list, sometimes unnecessarily, which increases memory consumption. Already the ifilter, the generating expressions and generators do not create or compute anything before it is time to actually use the results (and do not create persistent memory lists).

Imperative style (Generator):

def impar(elems):
    for i in elems:
        if i % 2:
            yield i

for x in impar(range(1000)):
    ...

Functional style (ifilter):

for x in ifilter(lambda i: i % 2, range(1000)):   # Python 2
    ...
for x in filter(lambda i: i % 2, range(1000)):    # Python 3
    ...

Declarative style (generating expression):

for x in (i for i in range(1000) if i % 2):
    ...

4

The answer depends somewhat on the version of Python you are using. In Python3 the filter was redone using itertools.ifilter python 2.x.

The great advantage of ifilter is the smallest use of memory because the list is produced as it is iterated.

# exemplo em Python 2.7
from itertools import ifilter
# lista de cem mil numeros (seria melhor ainda com xrange)
numbers = range(100000)

[n for n in numbers if n % 2 == 0]
# ==>  [0, 2, 4, 6, 8, ... 99998]
filter(lambda x: x%2 == 0, numbers)
# ==>  [0, 2, 4, 6, 8, ... 99998]

# usando ifilter (ou filter em Python 3)
ifilter(lambda x: x%2 == 0, numbers)
# ==>  <itertools.ifilter object at 0x10d06ce10>
# A lista não foi criada, para ler os elementos é preciso iterar
for n in ifilter(lambda x: x%2 == 0, numbers):
    print n
# ==>  0
# ==>  2
# ==>  4
# ==>  ...
# ==>  99998  

Apart from the aspects of efficiency in the use of memory and aesthetics, I remember nothing else that differentiates the two alternatives.

  • 2

    +1, but it should be mentioned that there is an equivalent to list understandings that has the same efficiency as the ifilter - the generating expressions.

  • 2

    Well remembered @mgibsonbr. I always remember function generators but I don’t even notice when I’m wearing expressions generators.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.