Up: Python

First Class Functions

slide 001 Hello, and welcome to the ninth episode of the Software Carpentry lecture on Python. In this episode, we’ll take a closer look at some things you can do with functions that will allow you to do more with less code.
slide 002 As we’ve seen in previous episodes, an integer is just 32 bits of data…
slide 003 …that a variable can refer to.
slide 004 And a string is just a sequence of bytes…
slide 005 …that variables can also refer to.
slide 006 Well, it turns out that a function is just another sequence of bytes—ones that happen to represent instructions…
slide 007 …and yes, variables can refer to them to.
slide 008 This insight—the fact that code is just another kind of data, and can be manipulated like integers or strings—is one of the most useful and powerful in all computing.
slide 009 To understand why, let’s have a closer look at what actually happens when we define a function.
slide 010 These two lines of code tell Python that threshold is a function that returns one over the sum of the values in signal.
slide 011 When we define it, Python translates the statements in the function into a blob of bytes, then creates a variable called threshold and makes it point at that blob.
slide 012 This is not really any different from assigning the string 'alan turing' to the variable name: the only difference is what’s in the memory the variable points to.
slide 013 Well, if threshold is just a reference to a value in memory, we should be able to assign that reference to another variable.
slide 014 Here’s our starting point once again.
slide 015 And here’s the assignment: t = threshold. As you can see, t now points to the same data as threshold: it is an alias for the function.
slide 016 To prove this is so, let’s try calling t. The result is exactly what we would get if we called threshold with the same parameters, because t and threshold are the same function.
slide 017 Here’s another thought: if the function is “just” data, can we put a reference to it in a list?
slide 018 Let’s define two functions, area and circumference, each of which takes a circle’s radius as a parameter and returns the appropriate value.
slide 019 Once those functions are defined, we can put them into a list like this. Of course, what we really mean is we’re copying the references to the functions stored in the variables area and circumference into the list, so that its first and second elements refer to the same blobs of instructions.
slide 020 We can now loop through the functions in the list, calling each in turn.
slide 021 Sure enough, the output is what we would get if we called area and then circumference with the parameter 1.0.
slide 022 Let’s go a little further. Instead of storing a reference to a function in a list, let’s pass that reference into another function, just as we would pass a reference to an integer, a string, or a list.
slide 023 Here’s a function called call_it that takes two parameters: a reference to some other function, and some other value. All call_it does is call that other function with the given value as a parameter.
slide 024 Let’s test it with area and 1.0: that’s right.
slide 025 And with circumference and 1.0: right again.
slide 026 So far, so pointless, but now it’s time for the payoff: functions of functions.
slide 027 Here’s a function called do_all that, as its name suggests, applies some function—anything at all that takes one argument—to each value in a list, and returns a list of the results.
slide 028 If we call do_all with area and a list of numbers…
slide 029 …we get what we would get if we called area directly on each number in turn.
slide 030 And if we define a function to “slim down” strings of text by throwing away their first and last characters…
slide 031 …we can apply it to every string in a list, without having to copy the code that loops through the list, calls the function, and concatenates the results.
slide 032 Functions that operate on other functions are called higher-order functions. They’re common in mathematics: integration, for example, is a function that takes some other function and two endpoints as parameters. In programming, higher-order functions allow us to re-use control flow rather than rewriting it.
slide 033 Here’s another example. combine_values takes a function and a list of values as parameters, and “adds up” the values in the list using the function provided, returning a single value as its result.
slide 034 To show how this works, let’s define add and mul to add and multiply values.
slide 035 If we combine 1, 3, and 5 with add, we get their sum, 9.
slide 036 If we combine the same values with mul, we get their product, 15. This same higher-order function combine_values could concatenate lists of strings, too, or multiply several matrices together, or whatever else we wanted, without us writing the loop logic ever again.
slide 037 Higher-order functions are a good thing because they let us do more with less code. If we don’t use higher-order functions…
slide 038 …then we have to write one function for each combination of data structure and operation, i.e., one function to add numbers, another to concatenate strings, a third to sum matrices…
slide 039 …and so on.
slide 040 With higher-order functions, on the other hand…
slide 041 …we only write one function for each basic operation, and one function for each kind of data structure…
slide 042 …and since A plus B is usually a lot smaller than A times B, this saves us coding, testing, and debugging.
slide 043 Of course, we have to know something about the function our higher-order function is operating on…
slide 044 …like how many arguments it takes.
slide 045 But in Python and many other languages, we can even get around that.
slide 046 Here’s a function called add_all that sums up values using plus.
slide 047 Notice the ‘*’ in front of the parameter args. This tells Python to take all of the parameters passed into the function when it’s called and put them together in a special type of list called a tuple, which we’ll explore in more detail in a couple of lectures.
slide 048 If we call add_all with no arguments, the tuple that’s assigned to args has no elements, so add_all returns 0.
slide 049 If we call add_all with the integers 1, 2, and 3, though, args has three elements, which add_all sums up to get 6.
slide 050 We can use this “catch-all” parameter with regular parameters, as long as the catch-all comes last.
slide 051 Here, for example, is another version of combine_values.
slide 052 The only difference between it and the previous version is the ‘*’ in front of the parameter values.
slide 053 This small change means that we don’t have to put the values we want to combine into a list before calling the function: the first actual parameter is assigned to func as before, and everything else goes into the tuple values.
slide 054 As an aside before we finish this episode, what do you think combine_values will do if we only provide a function, and no values for that function to operate on?
slide 055 More importantly, what do you think it should do?
slide 056 Several higher-order functions are actually built in to Python. One is filter, which constructs a new list containing all the values in an original list for which some function is true.
slide 057 Another is map, which applies a function to every element of a list, returning a list of results…
slide 058 …and then there’s reduce, which combines values using a binary function, returning a single value as a result.
slide 059 For example, if positive is True when its argument is greater than or equal to 0, then filter of positive and a list of numbers returns a list of non-negative numbers.
slide 060 If negate changes the sign of its argument, map of negate returns a list of negated values…
slide 061 …and of course, using reduce with add returns the sum of the values in the list.
slide 062 It usually takes a while to get used to working with higher-order functions, but while the ideas are digesting, let’s step back and ask, “What is programming anyway?”
slide 063 Novices usually think that it means writing instructions for a computer.
slide 064 But more experienced programmers think of it as creating and combining abstractions.
slide 065 When we’re programming, our goal is to spot a pattern, like “combine all elements of a list using a binary function”.
slide 066 And then write it down once, as clearly as possible…
slide 067 …so that we can build more patterns on top of it.
slide 068 Be cautious, though: the limits of human short-term memory still apply.
slide 069 If you pile too many abstractions on top of one another, it can be difficult to figure out what the end result actually does.
slide 070

  1. José Carlos García
    October 20th, 2010 at 17:59 | #1

    Hello, I’ve just seen this lecture and I think there’s a typing error: in the middle of the video, when you talk about higher-order functions, there’s an example with a function called ‘combine_values’. In the inside loop there’s a sentence: ‘current = func (current, v)’ but ‘v’ is not defined. Maybe it should be ‘values[i]‘ instead. Another possible correction could be ‘ for v in values[1:]:’ in the loop.

    Thanks A LOT for your project! I recommend it to all my colleagues.

  2. Terri Yu
    January 27th, 2011 at 23:24 | #2

    In the slide that defines the area and circumference functions, the variable “PI” is used. There is no variable PI in bare Python. You need to import the math library with a statement “import math” and then use “math.pi” or assign the value to another variable like “PI = math.pi”.

  1. No trackbacks yet.