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)