You Just Need a Little Closure
A closure is a function that retains some context at the time of its definition, so that it can refer to that later. You could say it “closes over” its arguments, and amazingly people actually do say that.
It’s been said both that “closures are a poor man’s objects”, and that “objects are a poor man’s closures”. Both accomplish fundamentally the same thing: expose some method(s) and retain some internal state. I have found that for many applications, a closure more closely represents my intent than a class.
If you’ve ever written a class that looks like this:
class Verber(object):
def __init__(self, prop1, prop2):
self.prop1 = prop1
self.prop2 = prop2
def verb(self, args):
# Do something with prop1, prop2, and args
pass
You could have written a closure that looks like this:
def make_verber(prop1, prop2):
def verb(args):
# Do something with prop1, prop2, and args
pass
return verb
The usage is almost identical:
verber = Verber('prop1', 'prop2')
verber.verb('args')
verb = make_verber('prop1', 'prop2')
verb('args')
You can also simulate classes with multiple methods, by simply returning either multiple functions, or some wrapper thereof:
def make_dog(name):
def sound():
return "Woof"
def name():
return name
return {'name': name, 'sound': sound}
dog = make_dog("Rover")
dog['sound']() # => "Woof"
dog['name']() # => "Rover"
Of course, this last example would offer nothing over a class but an awkward method-calling syntax, so let’s constrain our definition of a useful closure with a few rules:
- The encapsulated state should not be mutated, just stored for later.
- We will return just one method, unless we have a really good reason.
Sticking to these rules will allow you to defensibly choose closures over classes in certain cases. Treat closures as dynamically-generated functions (which is exactly what they are): you can initialize them with some useful state, and this will spare you passing around all that state if it’s only going to be used for one thing.
A short example
For a real-world example, here’s some code that could be rewritten with Closures from the (awesome) requests
library, dealing with different forms of authentication:
https://github.com/kennethreitz/requests/blob/master/requests/auth.py
class AuthBase(object):
"""Base class that all auth implementations derive from"""
def __call__(self, r):
raise NotImplementedError('Auth hooks must be callable.')
class HTTPBasicAuth(AuthBase):
"""Attaches HTTP Basic Authentication to the given Request object."""
def __init__(self, username, password):
self.username = username
self.password = password
def __call__(self, r):
r.headers['Authorization'] = _basic_auth_str(self.username, self.password)
return r
class HTTPProxyAuth(HTTPBasicAuth):
"""Attaches HTTP Proxy Authentication to a given Request object."""
def __call__(self, r):
r.headers['Proxy-Authorization'] = _basic_auth_str(self.username, self.password)
return r
Here, we have a base class, an implementing class, and another implementing class. However, notice that the only reason HTTPProxyAuth
extends HTTPBasicAuth
is to store the username and password – a pretty trivial reason to introduce a hierarchy here. Let’s see how it looks with closures:
def http_basic_auth(username, password):
"""Attaches HTTP Basic Authentication to the given Request object."""
def _inner(r):
r.headers['Authorization'] = _basic_auth_str(username, password)
return r
return _inner
def http_proxy_auth(username, password):
"""Attaches HTTP Proxy Authentication to a given Request object."""
def _inner(r):
r.headers['Proxy-Authorization'] = _basic_auth_str(username, password)
return r
return _inner
We’ve totally obviated the need for a base class, since we’re just working with functions. The username
and password
fields are no longer fields, but simply closed-over variables passed in directly as arguments. The username
and password
fields can no longer be accessed from the outside, which is arguably a feature.
One of the most common uses of closures in Python is writing decorators. In fact, this is probably the most obviously functional-programming thing that typical Python programmers do on a regular basis. Decorators are simply functions that wrap functions, performing some action before or after calling the function. So, the following:
@my_decorator
def my_function(*args):
pass
Accomplishes the same thing as this:
def _my_function(*args):
pass
my_function = my_decorator(my_function)
The my_decorator
implementation might look like this:
def my_decorator(some_fn):
def wrapped_fn(*args, **kwargs):
print "Calling wrapped function"
result = some_fn(*args, **kwargs)
print "Called wrapped function"
return result
return wrapped_fn
Often, decorators will be written that accept parameters:
@my_decorator("something")
def my_function(*args):
pass
In this case, my_decorator
could more accurately be thought of as a function that returns a decorator:
def my_decorator(s): # Takes a string and returns a decorator
def the_decorator(fn): # Takes a function and returns a new function
def wrapped_fn(*args, **kwargs): # Takes some arguments and calls the outer function on them
print "Calling fn from", s
result = fn(*args, **kwargs)
print "Calling fn from", s
return wrapped_fn
return the_decorator
While closures and objects can be considered equivalent in power, that doesn’t mean that a closure is the ideal solution in every case. However, many of the following chapters will be making extensive use of them, so it’s best to get comfortable now.
Next article: A Spicy Curry