Max
Max

Reputation: 13

Dynamic For Loops in Python

i understand that to create dynamic for loops, recursive or itertools module in python is the way to go. Lets say I am doing it in recursive.

What I want is

for var1 in range(var1_lowerlimit, var1_upperlimit, var1_stepsize):
    for var2 in range(var2_lowerlimit, var2_upperlimit, var2_stepsize):
    :
    :
        # do_whatever()

repeat for n loops where n is the number of variables

What I have now is I have 2 lists

variable_list = [ var1, var2, var3, ... ]
boundaries_list = [ [var1_lowerlimit, var1_upperlimit, var1_stepsize], 
                    [var2_lowerlimit, var2_upperlimit, var2_stepsize], ...]

def dynamic_for_loop(variable_list , boundaries_list, no_of_loops, list_index = 0):
    if no_of_loops <= 0:
        # do_whatever()
    else:
        lower_bound = boundaries_list[list_index][0]
        upper_bound = boundaries_list[list_index][1]
        step_size = boundaries_list[list_index][2]
        for index in range(lower_bound, upper_bound, step_size):
            list_index += 1
            try:
                dynamic_for_loop(variable_list , boundaries_list, no_of_loops - 1, list_index)
            except:
                list_index = 0
                dynamic_for_loop(variable_list , boundaries_list, no_of_loops - 1, list_index)

I did a reset on list_index as it gets out of range, but i couldn't get the result I want. Can someone enlighten me what went wrong?

Upvotes: 0

Views: 9700

Answers (2)

juanpa.arrivillaga
juanpa.arrivillaga

Reputation: 95948

Just for fun, I thought that I would implement this using recursion to perhaps demonstrate the pythonic style. Of course, the most pythonic way would be to use the itertools package as demonstrated by Martijn Pieters.

def dynamic_for_loop(boundaries, *vargs):
    if not boundaries:
        print(*vargs) # or whatever you want to do with the values
    else:
        bounds = boundaries[0]
        for i in range(*bounds):
            dynamic_for_loop(boundaries[1:], *(vargs + (i,)))

Now we can use it like so:

In [2]: boundaries = [[0,5,1], [0,3,1], [0,3,1]]

In [3]: dynamic_for_loop(boundaries)
0 0 0
0 0 1
0 0 2
0 1 0
0 1 1
0 1 2
0 2 0
0 2 1
0 2 2
1 0 0
1 0 1
1 0 2
1 1 0
1 1 1
1 1 2
1 2 0
1 2 1
1 2 2
2 0 0
2 0 1
2 0 2
2 1 0
2 1 1
2 1 2
2 2 0
2 2 1
2 2 2
3 0 0
3 0 1
3 0 2
3 1 0
3 1 1
3 1 2
3 2 0
3 2 1
3 2 2
4 0 0
4 0 1
4 0 2
4 1 0
4 1 1
4 1 2
4 2 0
4 2 1
4 2 2

Upvotes: 1

Martijn Pieters
Martijn Pieters

Reputation: 1122012

Use the itertools.product() function to generate the values over a variable number of ranges:

for values in product(*(range(*b) for b in boundaries_list)):
    # do things with the values tuple, do_whatever(*values) perhaps

Don't try to set a variable number of variables; just iterate over the values tuple or use indexing as needed.

Using * in a call tells Python to take all elements of an iterable and apply them as separate arguments. So each b in your boundaries_list is applied to range() as separate arguments, as if you called range(b[0], b[1], b[2]).

The same applies to the product() call; each range() object the generator expression produces is passed to product() as a separate argument. This way you can pass a dynamic number of range() objects to that call.

Upvotes: 5

Related Questions