Functions as Data

Dan Garcia, 2006-01-28

1. Nested Functions

You can define functions within other functions, if you want. Each new level gets its own set of local variables.

Here is a classic example:

def make_adder(x):
    def adder(y):
        return x + y
    return adder

Ask yourself: What does the make_adder function do?

Try these, and make sure you understand the results:

addthree = make_adder(3)

Consider this function:

def t(f):
    def g(x):
        return f(f(f(x)))
    return g

Ask yourself: What does t do? What does t(square)(7) return?

Figure out the answer before you try it yourself.

2. Functions as first-class objects

In the make_adder example above, we demonstrated a function that returned a function. The t function took in a function as an argument and returned another function! This demonstrates two of the features of a language that treats functions as first-class objects; they can be passed in to functions and returned from functions. This was a fundamental breakthrough in programming languages when it was introduced years ago; before that data was data and functions were functions and never the twain shall meet. That Python supports this feature allows us to do very powerful things, which we'll see later in the course.

3. Lambda forms

You can return a function without needing to write a nested function -- there's a Python "shorthand" that allows you to write small anonymous functions, and it's called lambda. Here is a small example:

plus = lambda x,y: x + y

which would define plus to be the function which takes two arguments (x and y) and returns the value of the expression x + y. This has the same result as if we had defined it the standard way:

def plus(x,y):
    return x + y

There are a few reasons the lambda form is so useful, which will become apparent as you work with higher-order functions later on (functions that take other functions as input). The first is that if you need to pass in a function as an argument to another function, it's a pain to use a def and have to come up with a name which will only be used once. Lambda lets you pass in the function directly with fewer keystrokes. The second occurs when you are returning a function -- it saves from having to define and return a nested function. Consider the definition of t. Look how simple it becomes when we use a lambda:

def t(f): 
    return lambda x: f(f(f(x))) 

There are other reasons to use a lambda, and the more you use it, the more you'll realize how handy it is.

If all of this seems completely foreign or overwhelming to you after you've experimented with it a bit, come on in to the Self-Paced Center and speak with a tutor.