tuple-driven processing (Python programming idioms)

Steven D. Majewski (sdm7g@elvis.med.virginia.edu)
Tue, 7 Dec 1993 12:27:07 -0500

_____________________________________________________________________
Another in a continuing series of "Favorite Python Programming Idioms".
Save Them! Collect Them! Trade them with your firends!
_____________________________________________________________________

I have found that "tuple-driven" code is a nice way to coerce
what would otherwise be a long string of special cases into
a short processing loop. Just collect all of the special case
information into a list or tuple of tuples, and loop over that
list.

This technique is usable in other languages, but I find I use
it frequently in Python because of the ease of creating the
initial tuple, and the fact that the dynamic nature of python
makes dynamic assignment possible. ( in other words, this sort
of thing is quite difficult in C, but quite easy also in Lisp.)

The example below is going to be part of a class library to
read tar files. (It is work in progress, and there is a bug in
it somewhere, but I'm using it as an example.) The _map tuple
are triples of ( fieldname, size-in-chars, interpret-as )
where interpret-as is 's'-string, 'o'-octal number, 'd'-decimal-number.
( and probably the bug is that I've got a decimal marked as
an octal, or something. )

Note: 'string.stringstrip' is string.strip renamed before trying
to import strop, in my (modified) version of string.py. Changing
string.whitespace ( or even strop.whitespace ) doesn't make
strop.strip "do the right thing". ( I didn't even BOTHER to try
to find a 'portable' way to do it this time! )

For each tuple in the set, a character field is extracted,
stripped, possibly eval-ed into an integer, and made into
an attribute ( instance variable ) of the class instance.

[ I have used this technique before to create dictionaries and
lists before, but the use of this with 'setattr' to create
a set instance variables is "new trick" that made me think
to post this example. ]

- Steve Majewski

import string
string.whitespace = string.whitespace + '\000'

_map = ( ( 'name', _NAMSIZ, 's' ),
( 'mode',8, 'o'), ('uid',8, 'o' ), ('gid',8, 'o' ),
( 'size', 12, 'd' ), ( 'mtime',12, 's'), ('chksum',8, 's'),
('linkflag',1, 's'),
( 'linkname', _NAMSIZ, 's' ),
( 'magic',8, 's'),
('uname',32,'s'), ('gname',32,'s'),
( 'devmaj',8,'o'), ('devmin',8,'o'))

class TarHeader:
def parse( self, h ):
i = j = 0
for item in _map:
i,j = j, j+item[1]
tmp = string.stringstrip( h[i:j] )
print item, tmp
if item[-1] == 'o' and tmp :
tmp = eval( '0'+tmp )
elif item[-1] == 'd' and tmp :
try: tmp = eval( tmp )
except OverflowError: tmp = eval( tmp+'L' )
if tmp or type(tmp) in ( type(1L), type(1) ) :
setattr( self, item[0], tmp )
return self.__dict__ # for debug