Higher-order functions and lambdas

INPUT · Slides

Keep only what matches

01 / 07

Narrowing down this time

map was for doing the same thing to everything. This is the other one: keeping only what you want.

With a comprehension you put an if on the end. The built-in function that does the same job is filter.

numbers = [3, -1, 5, -2]print([x for x in numbers if x > 0])

Result

[3, 5]

02 / 07

Hand a condition to filter

filter also takes a function and something you can loop over. The job of the function is different, though.

  • the function you give map … gives back something made over
  • the function you give filter … gives back whether to keep it

So you write a function that returns True or False. And what comes back is not a list, just as with map.

def positive(x):    return x > 0numbers = [3, -1, 5, -2]f = filter(positive, numbers)print(type(f))

Result

<class 'filter'>

03 / 07

This one hides its contents too

The trap is identical. Printing it as it is does not show you the contents.

numbers = [3, -1, 5, -2]print(filter(positive, numbers))<filter object at 0x1234abcd>

The address on the end changes every run. And this is not an error either — something unreadable simply comes out, quietly.

filter is a booking slip as well, saying "I will narrow these down for you". It checks one only when it is asked to.

04 / 07

Put it through list and you can see it

The fix is the same as well: put it through list(...).

Only the ones your function returned True for stay. The ones it returned False for never make it into the new run of values, so it usually comes out shorter than what went in.

def positive(x):    return x > 0numbers = [3, -1, 5, -2]print(list(filter(positive, numbers)))

Result

[3, 5]

05 / 07

Side by side with a comprehension

Put these next to each other too. The results are identical.

list(filter(lambda x: x > 0, numbers))
[x for x in numbers if x > 0]

The comprehension needs neither lambda nor list, which is why Python tends to prefer it — the very same conclusion as with map.

numbers = [3, -1, 5, -2]a = list(filter(lambda x: x > 0, numbers))b = [x for x in numbers if x > 0]print(a)print(b)print(a == b)

Result

[3, 5]
[3, 5]
True

06 / 07

Pass None and the empty ones drop out

filter has one odd way of being written: you can pass None where the function goes.

That means "drop the falsy values". Chapter 1 listed them — 0, empty text, None and False.

In the example below only the empty strings have gone. It is a common way to tidy up data with blanks mixed into it.

colours = ["red", "", "blue", ""]print(list(filter(None, colours)))

Result

['red', 'blue']

07 / 07

Combine it with map

You can stack them: narrow with filter, then transform with map. The trick is to read from the inside out.

As you can see, though, the parentheses get deep and it is hard going. The same thing as a comprehension is the one line [x * 2 for x in numbers if x > 0].

Stacking means a comprehension. That is the standard feeling in Python. In the next lesson we swap sides — from passing a function to receiving one.

numbers = [3, -1, 5, -2]print(list(map(lambda x: x * 2, filter(lambda x: x > 0, numbers))))

Result

[6, 10]