Closures in Python
September 18, 2006
I've previously written two tiny and not at all exhaustive posts about objects in Python (1, 2). Here's a similar discussion of another programming topic: closures. I think that closures are a bit more obscure than objects. The examples here are in Python, but you can do pretty much the same thing in many other languages.
Closures fall out of two things that Python can do. The first is that in Python we can treat functions as though there were data. (That's approximately what's meant when some people say that functions in Python are "first-class objects".)
If I define a function:
>>> def f(x):
... print x+1
I can assign it to a new name:
>>> g=f
And call the function with the new name:
>>> g(42)
43
I can put it in a list:
>>> l=[1,"a",f]
>>> l[2]
<function f at 0x4c670>
And call it from there:
>>> l[2](11)
12
It's not amazingly common, but it can occasionally be useful to put functions in various sorts of data structures. Perhaps a dictionary of functions indexed by the sort of data that they work on or produce.
(For example, I once had occasion to parse a calendar format. In calendar formats, repeating events (such as "2:00 pm on the last Thursday of the month") aren't recorded as dates and times since there could be an infinite number of instances of such an event. Instead, they're recorded as specifications for generating the actual instances. It was convenient for me to have the functions that would generate the next event in a dictionary that was indexed by the kind of repetition they did (daily, weekly, etc). But that's by the way for our purpose here.)
The second thing that closures depend on is that Python programs can have nested functions. That is, functions defined inside other functions:
>>> def outer(x):
... def inner(y):
... return y+2
... print inner(x)
...
>>> outer(10)
12
Nesting functions can be useful in making a program more readable. If a particular function can be made clearer by defining a helper function and the helper function is only going to be useful to the first function, it can make sense to define the helper inside the first function. That way someone who's reading your program and sees the helper doesn't need to wonder where it's going to be used.
Now, we can put those two facts together by having a function return a "customized" version of an inner function. For example:
>>> def outer(x):
... def inner(y):
... return x+y
... return inner
...
>>> customInner=outer(2)
>>> customInner(3)
5
The trick that you want to notice in what's going on there is what happens to the value of x. The argument x is a local variable in outer() and the behavior of local variables isn't normally very exciting. But in this case, x is global to the function inner(). And since inner() uses the name, it doesn't go away when outer() exits. Instead inner() captures it or "closes over" it. You can call outer() as many times as you like and each value of x will be captured separately.
The function that's returned is called a closure. The idea is potentially useful because we can specify part of the behavior of a function based on data at runtime.
At this stage you might say, "OK, I’ve followed your tedious explanation, but what good is such a thing? Is it anything more than a curiosity for ordinary programming?" The answer to that is that it is occasionally useful when something, such as a library interface, requires a function and you want to specify more than a couple of them that are very similar.
Imagine that you're designing a GUI interface and you need six buttons that do similar things. Tkinter buttons take a function to call as an argument and it would be tedious to write six very similar functions. Instead you might do something like this:
from Tkinter import *
def makeButtonFunc(buttonName):
def buttonFunc():
print buttonName
return buttonFunc
class mainWin:
def __init__(self,root):
self.root=root
self.createWidgets()
return None
def createWidgets(self):
for buttonName in ("A","B","C","D","E","F"):
b=Button(self.root,text=buttonName,
command=makeButtonFunc(buttonName))
b.pack()
return None
def main():
root=Tk()
mainWin(root)
root.mainloop()
return None
if __name__=="__main__":
main()
That's clearly better than writing six functions that are nearly identical.
There are lots of people who like using closures. I, personally, don't. To me, it feels like using a subtle trick and I prefer my programs to be as obvious as possible. In a similar situation, I'd use a Python object with a __call__() method. If a Python object has that method and it's called as though it were a function, that method is run. In a program I wrote, I'd probably replace makeButtonFunc() with something like:
class makeButtonFunc:
def __init__(self,buttonName):
self.buttonName=buttonName
def __call__(self):
print self.buttonName
Which would do the same thing. Of course, I'd give the class a different name.