lambda, map and filter
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.
1def double(n):2 return n * 23 4twice = double5print(twice(5))6print(double(5))
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:
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"]))
[2, 4, 6] ['lamp!', 'ore!']
What does this print?
1f = len2print(f("town"))
f = len gives the built-in len a second name. Calling f("town") is the same as calling len("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.
1double = lambda n: n * 22add = lambda a, b: a + b3print(double(5))4print(add(3, 4))
10 7
def | lambda | |
|---|---|---|
| Name | def double(n): | none; you can store it in a variable |
| Parameters | in parentheses | between lambda and the colon |
| Body | as many lines as you need | one expression |
| Result | return ... | 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.
1words = ["warehouse", "ore", "lamp", "mine"]2print(sorted(words))3print(sorted(words, key=len))4print(sorted(words, key=len, reverse=True))
['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:
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]))
[('Ada', 12), ('Cleo', 21), ('Ben', 30)]
('Ben', 30)
('Ada', 12)
What does this print?
1numbers = [5, -8, 3, -1]2print(sorted(numbers, key=abs))
key=abs sorts by distance from zero: 1, 3, 5, 8. The key is only used for comparing, so the list still holds the original numbers, minus signs and all.
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.
1text = "3 8 2 6"2loads = list(map(int, text.split()))3print(loads)4print(sum(loads))5print(list(map(lambda n: n * 10, loads)))
[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:
1results = map(str.upper, ["iron", "gold"])2print(list(results))3print(list(results))
['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().
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)))
[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:
| Job | map / filter | Comprehension |
|---|---|---|
| Change every item | list(map(lambda n: n * 2, loads)) | [n * 2 for n in loads] |
| Keep some items | list(filter(lambda n: n > 5, loads)) | [n for n in loads if n > 5] |
| Both | list(map(lambda n: n * 2, filter(lambda n: n > 5, loads))) | [n * 2 for n in loads if n > 5] |
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)
[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
lambdainsidemaporfilter, write a comprehension instead. - If the function already has a name, like
int,str.upperor one of your own,map(int, parts)is short and perfectly fine. key=lambda ...insorted,maxandminis 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.
Cheapest first
✓Print the shop's items sorted by price, cheapest first.
[('rope', 3), ('lamp', 12), ('pickaxe', 25)]1items = [("lamp", 12), ("rope", 3), ("pickaxe", 25)]2print(sorted(items))
Add up a line
✓Use map to turn the pieces of line into numbers, then print their sum.
50
1line = "4 15 8 23"2parts = line.split()3print(parts)
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.)
['Mill', 'Square']
1oil = {"North": 5, "Mill": 0, "Bridge": 2, "Square": 0}2print(list(oil))
What you learned
- A function is a value: a name without parentheses is the function, with parentheses it is a call.
lambda params: expressionmakes a small function with no name; it returns the expression.sorted,.sort,maxandmintakekey=, a function that says what to compare.map(f, items)callsfon every item;filter(f, items)keeps the items wherefis truthy. Wrap them inlist(), and use each only once.- When you would need a lambda for
maporfilter, a comprehension is usually clearer.