Closures and function Currying

Defining specialized functions on the fly

Closures

“Closures” and “Currying” are cool CS terms for what is really just defining functions on the fly.

you can find a “proper” definition here:

http://en.wikipedia.org/wiki/Closure_(computer_programming)

but I even have trouble following that.

So let’s go straight to an example:

def counter(start_at=0):
    count = [start_at]
    def incr():
        count[0] += 1
        return count[0]
    return incr

What’s going on here?

We have stored the start_at value in a list.

Then defined a function, incr that adds one to the value in the list, and returns that value.

[ Quiz: why is it: count = [start_at], rather than just count=start_at ]

So what type of object do you get when you call counter()?

In [37]: c = counter(start_at=5)

In [38]: type(c)
Out[38]: function

So we get a function back – makes sense. The def defines a function, and that function is what’s getting returned.

Being a function, we can, of course, call it:

In [39]: c()
Out[39]: 6

In [40]: c()
Out[40]: 7

Each time is it called, it increments the value by one.

But what happens if we call counter() multiple times?

In [41]: c1 = counter(5)

In [42]: c2 = counter(10)

In [43]: c1()
Out[43]: 6

In [44]: c2()
Out[44]: 11

So each time counter() is called, a new function is created. And that function has its own copy of the count object. This is what makes in a “closure” – it carries with it the scope in which is was created.

the returned incr function is a “curried” function – a function with some parameters pre-specified.

functools.partial

The functools module in the standard library provides utilities for working with functions:

https://docs.python.org/3.5/library/functools.html

Creating a curried function turns out to be common enough that the functools.partial function provides an optimized way to do it:

What functools.partial does is:

  • Makes a new version of a function with one or more arguments already filled in.
  • The new version of a function documents itself.

Example:

def power(base, exponent):
    """returns based raised to the give exponent"""
    return base ** exponent

Simple enough. but what if we wanted a specialized square and cube function?

We can use functools.partial to partially evaluate the function, giving us a specialized version:

square = partial(power, exponent=2) cube = partial(power, exponent=3)