Kodewick
Lesson 19 of 31Contents

lambda, map and filter

Lesson 19 of 31 · Part 3 · Functions · about 20 minutes

In Python a function is a value, just like a number or a list. You can give it a second name, store it, and hand it to another function. This lesson uses that idea three ways: short lambda functions, sorting with a key, and the built-in functions map and filter.

Functions are values

The name of a function, without parentheses, is the function itself. With parentheses, you call it.

main.py
1def double(n):2    return n * 23 4twice = double5print(twice(5))6print(double(5))
Output
10
10

twice = double does not run anything and does not make a copy. It gives the same function a second name, so twice(5) and double(5) do exactly the same thing.

Because a function is a value, you can pass it to another function as an argument. Here apply_to_all does not know what it will do to each item; whoever calls it decides:

main.py
1def double(n):2    return n * 23 4def exclaim(text):5    return text + "!"6 7def apply_to_all(func, items):8    results = []9    for item in items:10        results.append(func(item))11    return results12 13print(apply_to_all(double, [1, 2, 3]))14print(apply_to_all(exclaim, ["lamp", "ore"]))
Output
[2, 4, 6]
['lamp!', 'ore!']
Guess first

What does this print?

1f = len2print(f("town"))

lambda

A lambda is a small function written as one expression. It has no name of its own and no return: the value of the expression is what it returns.

main.py
1double = lambda n: n * 22add = lambda a, b: a + b3print(double(5))4print(add(3, 4))
Output
10
7
deflambda
Namedef double(n):none; you can store it in a variable
Parametersin parenthesesbetween lambda and the colon
Bodyas many lines as you needone expression
Resultreturn ...the expression, returned automatically

Storing a lambda in a variable, as above, works, but if a function deserves a name, def is clearer. A lambda is at its best when you write it right where it is used, as an argument to another function. That is what the rest of this lesson does.

Sorting with a key

sorted() and the list method .sort() take a key: a function that is called on each item. The items are then sorted by what that function returns, not by the items themselves.

main.py
1words = ["warehouse", "ore", "lamp", "mine"]2print(sorted(words))3print(sorted(words, key=len))4print(sorted(words, key=len, reverse=True))
Output
['lamp', 'mine', 'ore', 'warehouse']
['ore', 'lamp', 'mine', 'warehouse']
['warehouse', 'lamp', 'mine', 'ore']

With key=len, the words are compared by their lengths. lamp and mine have the same length, so they keep the order they had in the list.

When no existing function gives the value to sort by, write the key as a lambda. Here each miner is a tuple, and we sort by the second item. max() and min() take a key too:

main.py
1miners = [("Ada", 12), ("Ben", 30), ("Cleo", 21)]2by_ore = sorted(miners, key=lambda m: m[1])3print(by_ore)4print(max(miners, key=lambda m: m[1]))5print(min(miners, key=lambda m: m[1]))
Output
[('Ada', 12), ('Cleo', 21), ('Ben', 30)]
('Ben', 30)
('Ada', 12)
Guess first

What does this print?

1numbers = [5, -8, 3, -1]2print(sorted(numbers, key=abs))

map

map(function, items) calls the function on every item. It gives back a map object that hands out the results one at a time; wrap it in list() to get them all at once.

main.py
1text = "3 8 2 6"2loads = list(map(int, text.split()))3print(loads)4print(sum(loads))5print(list(map(lambda n: n * 10, loads)))
Output
[3, 8, 2, 6]
19
[30, 80, 20, 60]

The first map is a common pattern: split() gives strings, and map(int, ...) turns each one into a number. If you print a map object itself, you only see something like <map object at 0x...>.

A map object can be gone through only once. After list() has taken every result, nothing is left:

main.py
1results = map(str.upper, ["iron", "gold"])2print(list(results))3print(list(results))
Output
['IRON', 'GOLD']
[]

filter

filter(function, items) keeps the items for which the function returns True (or any truthy value) and drops the rest. Like map, it gives back an object you wrap in list().

main.py
1loads = [3, 8, 2, 6, 11]2print(list(filter(lambda n: n >= 5, loads)))3codes = ["12", "ab", "7", "3x"]4print(list(filter(str.isdigit, codes)))
Output
[8, 6, 11]
['12', '7']

Note that we pass str.isdigit, not str.isdigit(): filter needs the method itself, so it can call it on each code.

Comprehensions are often clearer

Everything map and filter do, a comprehension can do too:

Jobmap / filterComprehension
Change every itemlist(map(lambda n: n * 2, loads))[n * 2 for n in loads]
Keep some itemslist(filter(lambda n: n > 5, loads))[n for n in loads if n > 5]
Bothlist(map(lambda n: n * 2, filter(lambda n: n > 5, loads)))[n * 2 for n in loads if n > 5]
main.py
1loads = [3, 8, 2, 6, 11]2with_map = list(map(lambda n: n * 2, filter(lambda n: n > 5, loads)))3with_comp = [n * 2 for n in loads if n > 5]4print(with_map)5print(with_comp)
Output
[16, 12, 22]
[16, 12, 22]

Same result, but the comprehension reads left to right in plain words. A good rule:

  • If you would need a lambda inside map or filter, write a comprehension instead.
  • If the function already has a name, like int, str.upper or one of your own, map(int, parts) is short and perfectly fine.
  • key=lambda ... in sorted, max and min is where a lambda is most at home.

Practice

Write the code, then press Check. A hint costs nothing; the answer is there when you need it.

Exercise 1

Cheapest first

Print the shop's items sorted by price, cheapest first.

It should print
[('rope', 3), ('lamp', 12), ('pickaxe', 25)]
exercise1.py
1items = [("lamp", 12), ("rope", 3), ("pickaxe", 25)]2print(sorted(items))
Output
Press Run or Ctrl+Enter
Exercise 2

Add up a line

Use map to turn the pieces of line into numbers, then print their sum.

It should print
50
exercise2.py
1line = "4 15 8 23"2parts = line.split()3print(parts)
Output
Press Run or Ctrl+Enter
Exercise 3

Dark lamps

Use filter and a lambda to print a list of the lamps that have no oil left (0). (For practice; a comprehension would also do.)

It should print
['Mill', 'Square']
exercise3.py
1oil = {"North": 5, "Mill": 0, "Bridge": 2, "Square": 0}2print(list(oil))
Output
Press Run or Ctrl+Enter

What you learned

  • A function is a value: a name without parentheses is the function, with parentheses it is a call.
  • lambda params: expression makes a small function with no name; it returns the expression.
  • sorted, .sort, max and min take key=, a function that says what to compare.
  • map(f, items) calls f on every item; filter(f, items) keeps the items where f is truthy. Wrap them in list(), and use each only once.
  • When you would need a lambda for map or filter, a comprehension is usually clearer.