recursive function call

bruno at modulix onurb at xiludom.gro
Tue Nov 8 09:38:09 EST 2005


Nicolas Vigier wrote:
> Hello,
> 
> I have in my python script a function that look like this :
> 
> def my_function(arg1, arg2, opt1=0, opt2=1, opt3=42):
>         if type(arg1) is ListType:

How should it behave with tuples or subclasses of List ? Or if it's any
other iterable ?

Testing against the *exact* type of an object is usually not a good idea
in Python. You should at least replace this test with something like:
  if isinstance(arg1, list): #code here

or even better:
  if hasattr(arg1, '__iter__'): # code here


or still even better (for maintenance at least):

def my_function(arg1, ...):
  def kind_of_list(arg):
    return hasattr(arg1, '__iter__')

  if kind_of_list(arg1): # code here

>                 for a in arg1:
>                         my_function(a, arg2, opt1=opt1, opt2=opt2, opt3=opt3)
>                 return
>         if type(arg2) is ListType:
>                 for a in arg2:
>                         my_function(arg1, a, opt1=opt1, opt2=opt2, opt3=opt3)
>                 return
>          ... then here the real function code
> 
> I'm new to python, so I was wondering if there is a better way to do that.

> The reason for a recursive function here is to be able to give lists for
> the first 2 args 

<IMHO>
If possible, you'd better decide for a simplest API. Either always pass
a kind_of_list, or let the user take care of the loop.
</IMHO>

You can also use two functions, one that actually do the job and the
other that do the test and wrap the call to the first (note that the
first function doesn't need to be part of the public API). This makes
code more readable, and cleany decouple effective task from the
test_type_and_dispatch mechanism. ie:

def doTheJob(arg, opt1=None, opt2=None, opt3=None):
  # effective code here
  pass

def callDoTheJob(arg1, arg2, opt1=0, opt2=1, opt3=42):
   if is_kind_of_list(arg1):
     for a in arg1:
        callDoTheJob(a, arg2, ....)
   elif is_kind_of_list(arg2):
     for a in arg2:
        callDoTheJob(arg1, a, ....)
   else:
      doTheJob(arg1, arg2, ...)

> (I could as well use only a simple 'for' without a
> recursive call). 

This can be better when it comes to perfs. I find the recursive solution
to be more readable (but this is a matter of taste), and it has the
advantage/drawback (depends on the real usage) that nested lists are
handled for (almost) any depth of nesting.

> The problem I have here, is that as I'm working on this
> script, I often change the prototype of the function, and each time I
> have to think about changing the recursive call too. Is there a way that
> I can do a recursive call and say something like "keep all the same
> arguments except this one" ?

If this only concern the 'opts' args:

def callDoTheJob(arg1, arg2, **kw):
   if is_kind_of_list(arg1):
     for a in arg1:
        callDoTheJob(a, arg2, **kw)
   elif is_kind_of_list(arg2):
     for a in arg2:
        callDoTheJob(arg1, a, **kw)
   else:
      doTheJob(arg1, arg2, **kw)


hth
-- 
bruno desthuilliers
python -c "print '@'.join(['.'.join([w[::-1] for w in p.split('.')]) for
p in 'onurb at xiludom.gro'.split('@')])"



More information about the Python-list mailing list