Higher-order functions and lambdas

INPUT · Slides

Write a function that takes a function

01 / 07

So far you have always been the one passing

Line up what this chapter has covered.

  • sorted(names, key=len) … you passed the rule for sorting
  • max(people, key=lambda p: p["age"]) … you passed the rule for comparing
  • map(double, numbers) … you passed the work to apply
  • filter(positive, numbers) … you passed the condition for keeping

Every one of them handed a function to something that already existed. When the function you passed got called was hidden away inside, out of sight.

This lesson swaps the parts round. You write the side that receives.

02 / 07

Take a function as an argument

The secret is that the receiving side does nothing special at all. It puts parentheses on the argument it was given and calls it. That is the whole of it.

So far your arguments have been numbers and text. A function is a value too, so it arrives in exactly the same way.

The apply below does almost the same job as map. This time you can see everything that happens inside.

def apply(f, xs):    results = []    for x in xs:        results.append(f(x))    return resultsdef double(x):    return x * 2print(apply(double, [1, 2, 3]))

Result

[2, 4, 6]

03 / 07

Parentheses or no parentheses

Here, once more, is the most important distinction in the chapter.

  • double … the function itself. You can carry it about
  • double(3) … the result of running it. Here, 6

print the function itself and you get this.

print(double)<function double at 0x1234abcd>

The address on the end changes every time, as ever. There is nothing to read in it; all you need is that it is telling you "this is a function".

Slip the parentheses on when you pass it and what goes over is the result of calling it, not the function. You get a TypeError at the f(x), so when that happens, suspect this.

04 / 07

What happens next is for the caller to decide

The good thing about apply is that what it does can change without a single character of it changing.

Swap the function you pass and it doubles, or it counts letters. apply itself holds only the shape — "do something to every one" — and has no idea what that something is.

That is what "what happens next is for the caller to decide" means. It is the strongest idea there is for building functions as reusable pieces.

def apply(f, xs):    results = []    for x in xs:        results.append(f(x))    return resultsdef double(x):    return x * 2print(apply(double, [1, 2, 3]))print(apply(len, ["Yui", "Haruto"]))print(apply(lambda x: x + 1, [1, 2, 3]))

Result

[2, 4, 6]
[3, 6]
[2, 3, 4]

05 / 07

A condition passes just the same

What you pass need not be a way of making things over. Pass a function that returns True or False and you have built filter.

The only difference is that f(x) is used as the condition on an if. The skeleton — take it and call it — is exactly the same.

Neither map nor filter was magic. Inside, they are doing what you have just written.

def keep(f, xs):    results = []    for x in xs:        if f(x):            results.append(x)    return resultsprint(keep(lambda x: x > 0, [3, -1, 5, -2]))

Result

[3, 5]

06 / 07

Callbacks, in JavaScript terms

If you have touched JavaScript you will have spotted that this is the very same conversation.

Over there, a function passed in as an argument is called a callback function — the thing you were handing to forEach and map. Different name, identical work.

In Python the word "callback" is not as prominent as it is in JavaScript. Instead, a function that takes or gives back functions is called a higher-order function. sorted, map, filter and the apply you just wrote are all higher-order functions.

07 / 07

Chapter 10 in summary

Here is what the chapter has given you.

  • A function is a value. Without parentheses it is the function itself, so it goes into variables and gets passed about
  • A lambda makes a small nameless function on the spot
  • key= takes a rule, and settles how sorted, max and min sort and compare
  • map and filter do the same thing to everything and keep only what matches — through a list, mind
  • And the receiving side is yours to write as well

Once you see that the work itself can be an argument, how you build functions changes. Write the shape now and let someone else settle the contents later — you can make pieces like that now.

The next chapter gathers up tools that suddenly make life easier once you know them.

names = ["Minato", "Yui", "Haruto"]print(sorted(names, key=len))print(list(map(len, names)))print(list(filter(lambda n: len(n) == 6, names)))

Result

['Yui', 'Minato', 'Haruto']
[6, 3, 6]
['Minato', 'Haruto']