Retrofitting Abstraction

We interrupt our regular programming to speak geek.

Recently there’s been a lot of discussion about programming languages at work and Python is receiving a lot of attention for many reasons. I’ve been a big fan of Python for a long time, and the more Perl I see out in the wild, the less I like it: “There’s More Than One Way To Do It” doesn’t scale.

Since it has been two years since I wrote any Python in anger, I am feeling a bit rusty, but this example came to my attention today that reinforced my belief in Python’s coolness. Conventional wisdom is that a programmer should abstract away the implementation from a published API so that are no downstream dependencies to manage if the underlying implementation changes. In a language like Java, this typically means making all object attributes private and providing public accessor methods1.

In a large program this makes sense since setx(value) will perform additional operations such as validation behind the scenes. However, in a small program this involves writing huge amounts of boilerplate code that will have no benefit until the day the program reaches the size and importance where validating a value before setting the attribute becomes a useful. And even if you know in advance that accessor methods are going to be useful, they result in large amounts of unimportant code that future maintainers will need to read and understand.

The Python solution is typically elegant. In the beginning all public attributes are accessed directly: C.x = "foo" is more readable and quicker to type than C.setx("foo"). If you then decide that reading and writing to x needs to be controlled, then you write you accessor methods, define “x” as a property and Python ensures all those instances of C.x are handled using the correct accessor. Here’s an example from docs.python.org.

Class C(object):
    def __init__(self): self.__x = None
    def getx(self): return self._x
    def setx(self, value): self._x = value
    def delx(self): del self._x
    x = property(getx, setx, delx, "I'm the 'x' property.")

  1. aka “getters” and “setters” since reading a variable is usually accomplished using getx() and updating a variable is often called setx(value) [back]