I got some nice feedback from a recent post about scientific programming, and the thing that seemed to resonate most with the good folks out there, was the suggestion of using config files.
I want to go into some details of this in python only because I think it’s a pretty neat way of getting into dynamic programming (also meta-programming).
I bring this up because, as a scientific programmer, I was fed on a stringy diet of imperative programming (Fortran, and C), and bypassed the twisted power of Lisp (the mother, or meta, of all dynamic languages). Fortunately, the guys behind Python have sneaked some lisp-like dynamic features back into python. And these features make it very easy to do config files.
Here’s how I might abstract out a config file. Let’s say I’ve written a routine to run a simulation. First, I wrap everything in a function:
def simulation():
blahhh
if __name__ == '__main__":
simulation()
Then I pull out all the parameters I want to use
def simulate(pdb_file, n_step, force_field):
blahh
if __name__ == '__main__":
simulate('1jbc.pdb', 10000, 'OPLS')
With Python, I can collect all these parameters together into a dictionary:
parameters = {} # a dictionary
parameters['pdb'] = '1jbc.pdb'
parameters['n_step'] = 10000
parameters['force_field'] = 'OPLS'
def simulate(parameters):
do something with parameters['pdb']
and parameters['n_step'] etc.
if __name__ == '__main__":
simulate(parameters)
This seems like a major complication, but look what I’ve done. I’ve collected all the parameters into one thing – a dictionary. I can pass this whole dictionary to any other function, maybe
def analyze_results(parameters):
blah
or
def display_trajectory_in_a_viewer(parameters):
blah
This is really easy with Python’s dynamic typing, because the values in the dictionary can be anything, be it a string, or an integer, or a float. And when you save the parameters to a text file, it becomes, voila, a config file:
f = open('config', 'w')
parameters_string = repr(parameters)
f.write(parameters_string)
So what does the file ‘config’ look like?
{ 'pdb': '1jbc.pdb', 'n_step': 10000, 'force_field': 'OPLS' }
This is a legitimate piece of python code in text form. If you want, you can pad extra spaces and carriage returns and it won’t make a difference.
So now for the magic. Once you have saved your parameters in the text-file ‘config’, you read it back later in your program:
f = open('config', 'r')
config_string = f.read()
parameters = eval(config_string)
The parameters dictionary is as good as new.
The magic is in the function eval(). eval() turns a string of python code into pure python code. Turning code into strings, and back again is one of the cornerstones of meta-programming.
This allows you to save your parameters into a config file, and read it back again, with very little work. And now in another script, you can loop over any of the parameters as you please.
I think you’re not giving the `repr` command enough credit here. It’s the one that it converting the parameter dictionary into a string that can be later `eval`ed. Converting a live piece of data into a string that can be unambiguous turned back into data is no simple task.
What you’re doing here is data serialization using `repr` as the marshaller and `eval` as the unmarshaller. You could have also used python’s `pickle` methods or a YAML library to the same effect.
The real power of meta-programing comes when you start writing code that modifies or generates code. If you’ve ever used the C preprocessor to write a macro to compress some ugly idiom then you’ve meta-programmed.
Languages like Lisp, Scheme, Ruby and Python take this one step further, making it easy to modify running code on the fly. This opens up the possibility of writing your own domain specific languages or cleverly dispatching method calls without having to write them all explicitly.
Fun stuff!
I’ve found that I prefer eval() over pickle because the output is in a text form, that I can manipulate in TextMate. Pickle is in byte form as far as I remember.
I haven’t really tried YAML yet, but personally I don’t seem much of an advantage of YAML over nicely formatted Python structures.
instead of eval’ing why dont you just import the config file?
# in config.py
parameters = {‘a’:1,‘b’:2}
# in script.py
from config import parameters
Check out ConfigParser in the standard Python library.
