recursive function call

N

Nicolas Vigier

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:
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 (I could as well use only a simple 'for' without a
recursive call). 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" ?

Thanks

Nicolas
 
P

Peter Otten

Nicolas said:
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:
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 (I could as well use only a simple 'for' without a
recursive call). 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" ?

Here is a non-recursive approach:

def tolist(arg):
if isinstance(arg, list):
return arg
return [arg]

def f(arg1, arg2, more_args):
for arg1 in tolist(arg1):
for arg2 in tolist(arg2):
# real code

If it were my code I would omit the tolist() stuff and instead always pass
lists (or iterables) as the function's first two arguments.

Peter
 
D

Duncan Booth

Nicolas said:
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:
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 (I could as well use only a simple 'for'
without a recursive call). 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" ?

This gets messy if the actual first two arguments could also be lists, do
you really want it to recurse again? I would handle this by having two
functions, one which takes simple arguments:

def my_function(a, b, opt1=0, opt2=1, opt3=42):
... the real function code ...

and one which takes your list arguments:

def my_function_for_sequences(seqA, seqB, *args, **kw):
for a in seqA:
for b in seqB:
my_function(a, b, *args, **kw)

That way you avoid any confusion over the argument types. It won't handle
the case where only one of a or b is a sequence, but you can handle that
easily enough by creating a list in the call.

It will also handle the case where you pass an iterable which isn't a list,
so you have more flexibility there. (Once you are used to Python you should
get a big red warning light going on in your head any time you see an
explicit test for a type: not all such cases are bad, but they often
indicate a code smell.)
 
N

Nicolas Vigier

Peter Otten said:
Here is a non-recursive approach:

def tolist(arg):
if isinstance(arg, list):
return arg
return [arg]

def f(arg1, arg2, more_args):
for arg1 in tolist(arg1):
for arg2 in tolist(arg2):
# real code

If it were my code I would omit the tolist() stuff and instead always
pass lists (or iterables) as the function's first two arguments.

Hmm, that's right. After all it's not very useful to be able to give
a single element, passing a list all the time is more simple. That's
what I'll do. I got the idea of passing a list or a singe element
because that's what they do in SCons, but in my case it's not really
usefull.

Thanks
 
B

bruno at modulix

Nicolas said:
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
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,581
Members
45,056
Latest member
GlycogenSupporthealth

Latest Threads

Top