Re-thinking my if-thens - a software engineering question

Bruno Desthuilliers bdesth.quelquechose at free.quelquepart.fr
Wed Jan 24 16:12:58 EST 2007


metaperl a écrit :
> Ok, I have a module called textgen.py. The point of this module is to
> generate a csv file from an array of dictionaries.

Err... You know there's a csv module in the stdlib, don't you ?

> As I iterate through
> each dictionary, I "massage" the dictionary values before writing them
> out to csv. Now, for one dictionary entry, I have the following code:
> 
>             if dict_key == 'PCN':
> 		fields = dict_val.split("/")
>                 mo = re.match( '(\w{2})(\d{2})(\d{2})' , fields[1] )
> 		if mo:
> 		    dict_val = "%s/%s%s/%s" % (fields[0], mo.group(1), mo.group(3),
> fields[2][1:])
> 		else:
> 		    dict_val = dict_val

FWIW, you could as well skip this else clause

> Ok, so now here is what has happened. This code was based on the
> assumption that dict_val would have 2 forward slashes in it. It turns
> out that I need to follow a different process of massaging when no
> slashes are present. A naive solution would be something like:
> 
> if dict_key == 'PCN':
>     fields = dict_val.split("/")
>     if fields == 3:
       if len(fields) == 3:
>         dict_val = pcn_three(fields) # where pcn_three
> is the code above
>      else:
>          # new logic
> 
> But I am wondering if I should abstract the flow of control into a
> class or something.

Depends... If your code is starting to smell, then yes, it's probably 
time to refactor a bit. Note that you don't necessarily needs OO to 
clean up a mess. Python functions are objects on their own, so you can 
use them like any other variable. A common idiom is to use a dict as a 
dispatch table:

def test1(arg):
   print "test1", arg

def test2(arg):
   print "test2", arg

def test3(arg):
   print "test3", arg

dispatch = {
   'TEST_ONE': test1,
   'TEST_TWO': test2,
   'TEST_THREE': test3,
}
default = lambda arg: arg

#...

func = dispatch(dict_key)
dict_val = func(fields)

And using lambdas you can even come close to Perl !-)

dispatch = {
   'PCN' : lambda arg: \
           (new_logic, pcn_three)[arg.count('/') == 2](arg.split('/')),
   'XXX' : code_here,
   ...
}

Or you could write some "dispatcher helper" functions, like:

def dispatch_on_arg_count(default, *funcs):
   def _dispatcher(*args):
     try:
       func = funcs[len(args)]
     except IndexError:
       func =default
     return func(*args)
   return _dispatcher

dispatch = {
   'PCN': dispatch_on_arg_count(
            default,
            default,
            pcn_one,
            pcn_two,
            pcn_three
          ),
    'XXX' : code_here,
}


Or you could check Philip Eby's dispatch package...

And of course, since Python classes are themselves callables (no 'new' 
operator, you just call the class to instanciate it), and instances can 
be callable too (just define a __call__(self, ...) method), you can 
throw OO in the mix too  !-)

Now weither it will make your code more readable is another question...

> Ideas welcome.
> 

HTH



More information about the Python-list mailing list