Using closure for class privates?

Hello. I’m relatively new to Python, and find it to be a truly wonderful language. One thing I’m not so fond of, however, is the awkward syntax in class definitions. An alternative to the standard style of defining classes is to make use of the constructor’s function closure in a way that I usually do in Javascript, inspired to by Douglas Crockford’s Javascript The Good Parts.

Let me show you what I mean. I teach basic algorithms from the book by Sedgewick and Wayne that has a lot of example code in Java. Some of my students are more comfortable with Python than Java, and to help them, I ported some of the book’s code to Python. The following is my version of the book’s code for breadth-first search in a directed graph, written in the constructor closure style. (Scroll down for a “standard” Python class implementation if you’d like to start with that.)

import collections

class BFS:
    def __init__(self, G, s):
        edge_to = [-1] * G.V();
        dist_to = [-1] * G.V();

        q = collections.deque()
        q.append(s)
        dist_to[s] = 0
        while q:                      # while q not empty
            v = q.popleft()
            for w in G.adj(v):
                if dist_to[w] < 0:     # if w not visited
                    q.append(w)
                    edge_to[w] = v
                    dist_to[w] = dist_to[v] + 1

        def path(v):
            if dist_to[v] < 0:
                return None
            p = [v]
            while v != s:
                v = edge_to[v]
                p.append(v)
            p.reverse()
            return p

        self.has_path_to = lambda v: dist_to[v] >= 0
        self.dist_to = lambda v: dist_to[v]
        self.path_to = path

Of course, this is not how Python classes are usually written. Translating to what I understand as the standard style gives me this:

import collections

class BFS:
    def __init__(self, G, s):
        self.__s = s
        self.__edge_to = [-1] * G.V();
        self.__dist_to = [-1] * G.V();

        q = collections.deque()
        q.append(s)
        self.__dist_to[s] = 0
        while q:                      # while q not empty
            v = q.popleft()
            for w in G.adj(v):
                if self.__dist_to[w] < 0:     # if w not visited
                    q.append(w)
                    self.__edge_to[w] = v
                    self.__dist_to[w] = self.__dist_to[v] + 1

    def has_path_to(self, v):
        return self.__dist_to[v] >= 0

    def dist_to(self, v):
        return self.__dist_to[v]

    def path_to(self, v):
        if self.__dist_to[v] < 0:
            return None
        p = [v]
        while v != self.__s:
            v = self.__edge_to[v]
            p.append(v)
        p.reverse()
        return p

The closure version is, in my view, much cleaner than the standard version, riddled with self parameters and self.__ prefixes which are easy to forget (it took me several iterations to get them all in place during the translation) and distracting to the reader. Also, the closure version makes the private data truly private, rather than protected by awkward naming conventions.

But maybe I’m overlooking reasons not to use the closure style. Performance factors perhaps? I welcome your opinions.

1 Like

I see what you mean about the closure style avoiding the need for “self.”, but because it does, it makes it hard to distinguish between object state and locals. Of course, there’s no need for the double-underscore prefix. I find it usually doesn’t pay to be obsessive about “private” attributes.

Your closure implementation has another drawback, which is that it would be nearly impossible to examine your “object” in a debugger.

I would keep the standard version, but without the double underscores.

1 Like

It’s true that this technique might require some work in debugging tools, but it would be no more difficult than for a, say, Java debugger. Anyway, I’m not letting that stop me. :slight_smile:

1 Like

Since I didn’t get a lot of response here, I did some more work trying this pattern with decorators, and some experiments. And I wrote a little text about it that should be easy to read, accompanied by examples, here: An alternative pattern for objects in Python.

1 Like