When to use list comprehension and not filter + lambda?

6

Good evening, everyone. Studying here, I found the List Comprehension and filter + lambda functions to be fantastic, but they both seemed pretty similar to me on some points ...

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

    
asked by anonymous 16.10.2015 / 02:22

2 answers

3

The answer depends a little on the version of Python you are using. In Python3% w / o of% was redone using filter of Python 2.x.

The great advantage of itertools.ifilter is the lower memory usage because the list is produced as it is being 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  

Aside from the efficiency aspects of memory usage and aesthetics, I do not remember anything else that differentiates between the two alternatives.

    
16.10.2015 / 04:57
3

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

Imperative style :

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

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

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

Personally, I find list understanding more "light-hearted," so I never write code using filter (mostly filter + lambda ).

Regarding efficiency issues pointed out by sergiopereira , in fact filter and list comprehensions create a new list, sometimes unnecessarily, which increases memory consumption. Since ifilter , generating expressions and generators do not create or compute anything before it's time to actually use the results (and do not create persistent lists in memory).

Mandatory 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):
    ...
    
16.10.2015 / 05:48