List comprehensions vs filter() + lambda for list comprehensions


#1

Hello

I'm venturing into list comprehensions, lambda and the filter() function. I'm able to use all 3 of them and it looks like I understand them, but I don't feel like I fully get them.

Considering that we can couple filter() with a lambda to create a list comprehension of sorts, why do we even need list comprehensions in the first place?

Are there jobs which filter() + lambda can't achieve which a list comprehension could?


#2

Hi @lijok ,

List comprehensions, lambda and the filter functions are all useful to know. While you may be able to avoid using lambda by creating and using a named function, and avoid list comprehensions by utilizing a for loop, all three of the aforementioned can be helpful for writing concise code. With practice, those Python features become easier to understand and use.

Be aware that the filter function in Python 3 creates what is known as an iterator while the filter function in Python 2, which Codecademy teaches, creates a list.

See Python 2: filter and Python 3: filter.


#4

Hi @lijok,

An iterator is an object that specifies or defines a sequence, however it does not store the items that belong to the sequence in memory. In Python 2, the range function produces a list, and all the items in that list are stored in memory when the list is created. After the following line is executed in Python 2, nums will refer to a list of 1000000000 ints, all stored in memory ...

nums = range(1000000000)

However, in Python 3, the same line of code would instead assign an iterator to nums without actually creating and storing the 1000000000 ints that comprise the sequence. To retrieve the items, you could do this ...

for n in nums:
    print(n)

As the loop executes, each n is created and printed on the fly, one at a time. A list of 1000000000 ints is not stored in memory. Eachint is simply created, displayed, then discarded, using much less memory.

In Python 3, the filter function also produces an iterator, unlike what it does in Python 2.

Here is the another secret message program, adapted for Python 3 ...

# Example for Python 3
garbled = "IXXX aXXmX aXXXnXoXXXXXtXhXeXXXXrX sXXXXeXcXXXrXeXt mXXeXsXXXsXaXXXXXXgXeX!XX"

message = filter(lambda ch: ch != "X", garbled)

# This produces a disappointing result
print(message) 

# By the way, message was assigned a filter, a type of iterator

# This is nicer; is an example of retrieving values from an iterator
for ch in message:
    print(ch, end="")

Output ...

<filter object at 0x102744390>
I am another secret message!

#6

Used together, the filter function and lambda form a concise means of testing each element of a sequence to determine whether or not that element is to be included in a new sequence that is being created. That is the focus of the Lambda Expressions exercise. For the test, the lambda function returns either True or False. A list comprehension is useful for a situation wherein the issue, rather than being a question of inclusion or non-inclusion, is primarily a mapping from a value in an original sequence to another value to be placed in a new sequence. For example, we can make a list of cubes as follows ...

cubes = [n ** 3 for n in range(1, 21)]

The mapping is a transformation from one value to another. In the above case it is cubing the original number.

We could also include a test in a list comprehension. For example, if we only want cubes for which the final digit is not a 1, we could do this ...

cubes = [n ** 3 for n in range(1, 21) if n ** 3 % 10 != 1]
print(cubes)

Output ...

[8, 27, 64, 125, 216, 343, 512, 729, 1000, 1728, 2197, 2744, 3375, 4096, 4913, 5832, 6859, 8000]

Alternatively, we could use the filter function and a lambda here to perform the test of the final digit, however that would be less concise for this example.


#8

There's actually lots of flexibility here. If you want, you could include a list comprehension as the second argument to the filter function, so that you can create a list on the fly and filter it immediately. Try this in Python 3 ...

limit = int(input("Highest number to cube: "))
final_digit_to_exclude = int(input("Final digit to exclude: "))
result = filter(lambda n: n % 10 != final_digit_to_exclude, [n ** 3 for n in range(1, limit + 1)])
print(list(result))

Output ...

Highest number to cube: 23
Final digit to exclude: 1
[8, 27, 64, 125, 216, 343, 512, 729, 1000, 1728, 2197, 2744, 3375, 4096, 4913, 5832, 6859, 8000, 10648, 12167]

A good approach is to experiment with these features until you are comfortable with them. Use Google to find examples that you can refine in order to enhance your understanding. Then, when you write programs, you can decide when and where to use these features.

EDITED (June 20, 2017) to convert result to a list in the final example prior to output.


#10

When deciding what to use, favor simplicity and readability. If the code might be used to handle a large sequence, consider efficiency and memory usage. Try to achieve a balance between various considerations, such as ...

The Zen of Python

import this

Output ...

Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!

See Wikipedia: Zen of Python.


#12

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.