I'm trying to run a function with all possible combinations of its parameters. In Python, I'm running into the limit of 20 nested for-loops. Every post on the 'Net says "You're doing it wrong if you hit that limit." So what's the right way to do it?
def function_to_test( **kwargs ):
print kwargs
parms = {'parameterA': (True, False),
'parameterB': (True, False),
'parameterC': (True, False)}
for np in range( len( parms ) ):
func_str = '\n'
p0 = -1
for p in range( np + 1 ):
func_str += 2*p*' ' + "for p%d in range( p%d + 1, len( parms ) ):\n" % (p + 1, p)
func_str += (2*p + 1)*' ' + "key%d = parms.keys()[p%d]\n" % (p + 1, p + 1)
func_str += (2*p + 1)*' ' + "vals%d = parms[key%d]\n\n" % (p + 1, p + 1)
func_str += (2*p + 1)*' ' + "for use_val%d in vals%d:\n\n" % (p + 1, p + 1)
func_str += 2*(np + 1)*' ' + "cmd = \"function_to_test("
for p in range( np + 1 ):
func_str += "%s=%s"
if p < np:
func_str += ", "
func_str += ')" % ('
for p in range( np + 1 ):
func_str += "key%d, use_val%d, " % (p + 1, p + 1)
func_str += ")\n"
func_str += 2*(np + 1)*' ' + "exec( cmd )\n\n"
exec func_str in globals(), locals()
This runs as I want it to:
{'parameterA': True}
{'parameterA': False}
{'parameterC': True}
{'parameterC': False}
{'parameterB': True}
{'parameterB': False}
{'parameterA': True, 'parameterC': True}
{'parameterA': True, 'parameterC': False}
{'parameterA': True, 'parameterB': True}
{'parameterA': True, 'parameterB': False}
{'parameterA': False, 'parameterC': True}
{'parameterA': False, 'parameterC': False}
{'parameterA': False, 'parameterB': True}
{'parameterA': False, 'parameterB': False}
{'parameterC': True, 'parameterB': True}
{'parameterC': True, 'parameterB': False}
{'parameterC': False, 'parameterB': True}
{'parameterC': False, 'parameterB': False}
{'parameterA': True, 'parameterC': True, 'parameterB': True}
{'parameterA': True, 'parameterC': True, 'parameterB': False}
{'parameterA': True, 'parameterC': False, 'parameterB': True}
{'parameterA': True, 'parameterC': False, 'parameterB': False}
{'parameterA': False, 'parameterC': True, 'parameterB': True}
{'parameterA': False, 'parameterC': True, 'parameterB': False}
{'parameterA': False, 'parameterC': False, 'parameterB': True}
{'parameterA': False, 'parameterC': False, 'parameterB': False}
I know this is horrifying. Don't even ask how long it took to write and debug. But, given that I want to test all combinations, I want the list of parameters and values to be dynamic, and I can't modify the function_to_test
, how else could I approach this problem?
Addendum:
Here's what func_str
contains for the loop where np=1
(i.e., it's running function_to_test
with all combinations of two parameters). Hopefully this will somewhat clarify what's happening in my existing code.
for p1 in range( p0 + 1, len( parms ) ):
key1 = parms.keys()[p1]
vals1 = parms[key1]
for use_val1 in vals1:
for p2 in range( p1 + 1, len( parms ) ):
key2 = parms.keys()[p2]
vals2 = parms[key2]
for use_val2 in vals2:
cmd = "function_to_test(%s=%s, %s=%s)" % (key1, use_val1, key2, use_val2, )
exec( cmd )
Solution:
The recursive way, as suggested by @dasblinkenlight.
def do_all( myargs, level ):
if level == len( parms ):
function_to_test( **myargs )
else:
key = parms.keys()[level]
for v in range( len( parms[key] ) + 1 ):
if v < len( parms[key] ):
myargs[key] = parms[key][v]
else:
del myargs[key]
do_all( myargs, level + 1 )
do_all( {}, 0 )