36

I am trying to test some other Python code repeatedly, using all possible combinations of values for six different parameters. For each parameter I want to iterate over a range of values with a given minimum, maximum and step.

I managed to write some code like:

for var1 in range(min1, max1, step1):
    for var2 in range(min2, max2, step2):
        for var3 in range(min3, max3, step3):
            for var4 in range(min4, max4, step4):
                for var5 in range(min5, max5, step5):
                    for var6 in range(min6, max6, step6):
                        do_something_with(var1, var2, var3, var4, var5, var6)

But I do not like that the code is so deeply nested.

How can I avoid nesting multiple loops? Can it be done using recursion? How about itertools.product? I wasn't able to figure out either approach.


See also, more generally: Get the cartesian product of a series of lists?

This question, and some answers, originally showed code for Python 2.x. It has been edited because the fundamental problem persists in 3.x, with the same solution, but xrange no longer exists - range is the drop-in replacement. For more information, see What is the difference between range and xrange functions in Python 2.X?.

Karl Knechtel
  • 62,466
  • 11
  • 102
  • 153
rapidsnow
  • 363
  • 1
  • 3
  • 5
  • 2
    what should be actually done inside all those loops? i think it is the most important question - there are some syntax sugars to hide the loops (but still performing them), but to avoid nested loops, the most important thing is to understant the original problem – Aprillion Jun 24 '12 at 03:34
  • 1
    @deathApril is right: I've given you a way to avoid the loops below, but you still have to do all those iterations. Better would be to use a different algorithm to avoid the iterations. – Ned Batchelder Jun 24 '12 at 03:36
  • Added an edit to explain why I need this – rapidsnow Jun 24 '12 at 03:41
  • 3
    The point is not about using or not using for loops; it is more that you may not understand how quickly the number of test cases increases. 6 settings, each with 10 possible values, is a million test cases. – Hugh Bothwell Jun 24 '12 at 03:49
  • i see the itertools.product solved your problem. good for you. just don't be surprised when you find out the performance difference between explicit nested for loops and hidden C code that performs nested loops can only be so big ;) P.S.: actually, it would be nice if you did post the performance test results :)) – Aprillion Jun 24 '12 at 03:55
  • 1
    @deathApril: you're the only one talking about performance. The OP didn't mention is at all, this isn't about performance, it's about the structure of the code. – Ned Batchelder Jun 24 '12 at 12:13

3 Answers3

55

Here's how to use product:

x1 = range(min1, max1, step1)
x2 = range(min2, max2, step2)
x3 = range(min3, max3, step3)
...

for v1, v2, v3, v4, v5, v6 in itertools.product(x1, x2, x3, x4, x5, x6):
    do_something_with(v1, v2, v3, v4, v5, v6)

or a bit more compactly:

ranges = [
    range(min1, max1, step1),
    range(min2, max2, step2),
    range(min3, max3, step3),
    ...
]

for v1, v2, v3, v4, v5, v6 in itertools.product(*ranges):
    do_something_with(v1, v2, v3, v4, v5, v6)
Karl Knechtel
  • 62,466
  • 11
  • 102
  • 153
Ned Batchelder
  • 364,293
  • 75
  • 561
  • 662
17

You can probably use itertools.product. Something like

for var1, var2 in itertools.product(range(min1, max1, step1), range(min2, max2, step2)):
    # stuff

but with all six vars in there.

Karl Knechtel
  • 62,466
  • 11
  • 102
  • 153
BrenBarn
  • 242,874
  • 37
  • 412
  • 384
-1

you can use multiprocessing and itertools.product to speed up and simplify your code

import itertools
from multiprocessing import Pool

ranges = [
    range(min1,max1,step1),
    range(min2,max2,step2),
    range(min3,max3,step3),
    ...
]

with Pool(os.cpu_count()-2) as p:
    result = p.map(your_op_func, itertools.product(*ranges))
  1. multiprocessing can speed up your task (because it's mutex task)
  2. itertools.product can help you get combinations through a more effectively way
DharmanBot
  • 1,066
  • 2
  • 6
  • 10
zhangjq
  • 132
  • 1
  • 6