Easily extensible command module through reflection
Mark Côté, Wednesday, July 15th, 2009
The wikipedia describes reflection as “the process by which a computer program can observe and modify its own structure and behavior.” Sounds on the surface like some crazy theoretical computer science, but it’s actually easy to use reflection in real-life applications. At the risk of making this blog Python specific, I’ll show you a simple example of how you can use Python’s reflective character to create an easily extensible module for executing commands. This isn’t particularly advanced, but it does show the power of the current generation of programming languages.
Say you have some sort of application that supports a number of different commands–through network protocols or parsed files or the like. It’s not a heavy program, so you have one class that is responsible for executing the code associated with each command: you want to avoid the boilerplate of having an object for each command. In C++, each time you’d want to add a command, in addition to writing the function (and of course the declaration in the header file), you would have to add to a growing switch statement an association between the command name (probably declared as a literal somewhere to avoid spelling mistakes) and the command function. So that’s 3 or 4 things that you need to change just to make your app support a new command, and you get the ugliness of a long switch statement.
In Python, thanks to its reflective capabilities, you can create a command-execution function that requires exactly one change to add a new command: adding a new function with an appropriate name (and of course Python has no header files). This is due to the magic of getattr:
class CommandRunner(object):
...
def RunCommand(self, cmd_name, args):
cmd_func_name = 'Do_' + cmd_name
try:
cmd_func = getattr(self, cmd_func_name)
except AttributeError:
# handle non-existent command
try:
cmd_func(args)
except:
# handle exception executing command
You want to support a new command named “foo”? Just create CommandRunner.Do_foo()! If you’ve properly set up your exception handling for AttributeErrors, you can easily error out on nonexistent commands, while seamlessly executing supported commands.
Metaprogramming–no longer just for eggheads!
July 15th, 2009 at 15:02
You can do this in a few lines of C by using dlsym/GetProcAddress. Doing so also forces you to separate your platform dependent RPC/IPC layer from your likely platform independent command implementations.
September 10th, 2009 at 00:05
@Steven: (sorry for the ultralate reply) Ah true. I should say, perhaps, that all this stuff is more obvious in Python. Plus it’s inherently platform independent, with one way to do it regardless of the underlying OS/machine. And finally, from my understanding it isn’t so easy to get information on class variables, or, at least, it can’t be done in the same way. Perhaps I’m wrong… but that’s again my main point–it’s all much more obvious in, and integral to, Python.