2

I'm working on a script to find optimum parameters to be passed to a method.I have created a list of possible values of parameters I would like to iterate over, and was wondering if I could avoid writing nested loops for the same.

Here's a rough sketch of the code

param_1 = [1,2,3,5,10]
param_2 = [100,200]
param_3 = [True, False]
param_4 = [True, False]
.
.

for p1 in param_1:
  for p2 in param_2:
      .
      .
      do_something(p1,p2,...)

I was wondering how it can be implemented in a better way. I would prefer if in the solution, I can easily shuffle which parameter stays in the outermost loop. etc.

Ashwini Khare
  • 1,645
  • 6
  • 20
  • 37
  • Do you need to test every combination or permutation? – TigerhawkT3 Jun 09 '15 at 22:34
  • 1
    Why do you want to avoid nested loops exactly? – River Jun 09 '15 at 22:35
  • 2
    Then I would recommend looking into `itertools.combinations` and `itertools.permutations`, but not before taking a step back and making absolutely sure you're solving the problem the right way. – TigerhawkT3 Jun 09 '15 at 22:37
  • 2
    `for p in itertools.product(param_1, param_2, ...):` works, though it's simply a coat of paint on your same nested loops. – roippi Jun 09 '15 at 22:37
  • @River : So as to try all combinations of parameters to be passed, like Tigerhawk pointed out – Ashwini Khare Jun 09 '15 at 22:37
  • @River, it's fine to use a few nested loops, but more than that can become very messy and is almost always an indication of an incorrect algorithm. – TigerhawkT3 Jun 09 '15 at 22:38
  • You can use nested loops to test all combinations quite well. `itertools` is a good workaround if you want a different appearance, but it will be the same speed. – River Jun 09 '15 at 22:38
  • In Python, the appearance matters. – TigerhawkT3 Jun 09 '15 at 22:41
  • `itertools.product` sounds about right to me. – Paul Rooney Jun 09 '15 at 22:41
  • @TigerhawkT3 Yes it matters in all languages that are human read, but I wanted to make sure OP didn't have any illusions about being able to speed up code by avoiding nested loops. Just because it looks different doesn't mean it IS different. – River Jun 09 '15 at 22:43
  • 1
    I might posit that with the nested loops, it's pretty clear what's going on. it might not be as "elegant", but I never did much worry about that. Sometimes a bit more code is a lot more understandable. – CargoMeister Jun 09 '15 at 22:45
  • The OP didn't make any references to faster execution times. He only specified that he wanted to avoid awkward constructions and work with the code more easily. – TigerhawkT3 Jun 09 '15 at 22:46
  • @TigerhawkT3 He never said EITHER of those things. He said a "better implementation". This could refer to speed, appearance, or any number of other things. – River Jun 09 '15 at 22:47
  • 1
    @River : Sorry for the confusion. I had a better appearance in mind. In the current state I had like 10 nested loops which I thought was wrong. Speed isn't a concern here – Ashwini Khare Jun 09 '15 at 22:49
  • @River: exactly. He wanted to make it better, which could involve appearance, and you asked why he would want to avoid the nested loops. The answer is "appearance" (and ease of maintenance, etc.). It's a valid goal in the same way that avoiding `print(0); print(2)... print(9)` with `for num in range(10): print(num)` is superior, even though the former might even execute slightly faster. – TigerhawkT3 Jun 09 '15 at 22:50
  • And yes, the OP did mention awkward constructions and ease of maintenance, viz: "avoid writing nested loops" (which are awkward when piled up more than a few layers) and "easily shuffle" (a giant block of nested loops is not easy to work with). – TigerhawkT3 Jun 09 '15 at 22:52
  • @TigerhawkT3 You are assuming much (in this case correctly) from the OP's question. I was simply asking him to spell it out so no assumptions needed to be made (which he has now done). – River Jun 09 '15 at 22:55

1 Answers1

2

You can use itertools.product:

import itertools

for args in itertools.product(param_1, param_2, param_3, param_4):
    do_something(*args)

This creates a Cartesian product of all possible parameter combinations which are passed to do_something. You can also modify the order how the param_X variables are passed to itertools.product which influences the order of how the arguments will be passed to do_something.

Simeon Visser
  • 118,920
  • 18
  • 185
  • 180