Unpythonic

Functional patterns for the Python maverick

Last updated: Dec 6, 2015

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