Subclass ’em Functions

Quick Summary

I’ve got a simple Python helper class, in the talljosh package, that lets you write things that behave as functions but can be extended by subclassing.

But Why?

Consider the following Python function, found in json/encoder.py in the standard library:

Nested functions can be very useful for simple examples like this, but they don’t allow much flexibility. I can’t come along and say “I want to do exactly the same, but with a different dictionary for escaping. If the code author wanted to allow that, he’d have to do something like this:

But you can’t expect coders to parameterise every function in this way. It makes the code noisy and makes it hard to understand what’s typical usage.

One of the principles of Python programming is that those using your code are consenting adults. They don’t need to be treated like children. And therefore in Python you never worry about controlling access to methods or functions—you assume your user is grown-up enough to know what they’re doing, even if it’s not what you intended them to do.

Unfortunately, nested functions are hard to introspect, and very hard to extend.

What Then?

I’ve written a base class called Function which helps alleviate this problem.

To the casual user, this function behaves exactly the same as the original: you call it in the same way. Calling it will create an instance, and call the run() method. But it has the advantage of being extensible. I can subclass it and override ESCAPE, or ESCAPE_DCT, or even override replace().

How Does it Work?

The Function base class is very simple. (I stripped out the 22 lines of docstring for this post.)

By defining the __new__ method in this way, Function subclasses behave like normal functions when called. This means that users don’t have to type encode_basestring().run(s), but can simply call encode_basestring(s) and get the result.

The metaclass definition is only necessary so that you can use Function subclasses as methods. For example:

Notice that the methods inside the Function receive the running Function as self, and the enclosing MyClass instance as their second parameter.

In order for this to work, the metaclass looks like this:

This is descriptor magic, and is a topic for another day.

Where can I get it?

The talljosh package is licensed under version 2 of the GPL. If you accept the license, you can get it from the Python Package Index.

This entry was posted in midlength and tagged , , , . Bookmark the permalink.

One Response to Subclass ’em Functions

  1. Tanner Cereghino says:

    Wow, I like the way you put that.