In python is there an easier way to write 6 nested for loops?

Question:

This problem has been getting at me for a while now. Is there an easier way to write nested for loops in python? For example if my code went something like this:

  for y in range(3):
    for x in range(3):
      do_something()
      for y1 in range(3):
        for x1 in range(3):
          do_something_else()

would there be an easier way to do this? I know that this code works but when you indent instead of using 2 spaces, like me, it can get to be a problem.

Oh in the example there were only 4 nested for loops to make things easier.

Asked By: saleh

||

Answers:

When faced with that sort of program logic, I would probably break up the sequence of loops into two or more separate functions.

Another technique in Python is to use list comprehensions where possible, instead of a loop.

Answered By: Greg Hewgill

My personal argument would be that you’re likely doing something wrong if you have 6 nested loops…

That said, functional decomposition is what you’re looking for. Refactor so some of the loops happen in seperate function calls, then call those functions.

Answered By: Matthew Scharley

Assuming each loop has some sort of independent meaning, break them out into named functions:

def do_tigers():
    for x in range(3):
        print something

def do_lions():
    do_lionesses()
    for x in range(3):
        do_tigers()

def do_penguins():
    for x in range(3):
        do_lions()

..etc.

I could perhaps have chosen better names. 😎

Answered By: RichieHindle

Have you looked into List Comprehensions?

Something like:

[do_something() for x in range(3) for y in range(3)]
Answered By: Yancy

That way looks pretty straightforward and easy. Are you are saying you want to generalize to multiple layers of loops…. can you give a real-life example?

Another option I could think of would be to use a function to generate the parameters and then just apply them in a loop

def generate_params(n):
    return itertools.product(range(n), range(n))

for x,y in generate_params(3):
    do_something()
Answered By: Joe Koberg

you can also use the map() function

Answered By: jle

If you’re frequently iterating over a Cartesian product like in your example, you might want to investigate Python 2.6’s itertools.product — or write your own if you’re in an earlier Python.

from itertools import product
for y, x in product(range(3), repeat=2):
  do_something()
  for y1, x1 in product(range(3), repeat=2):
    do_something_else()
Answered By: Alice Purcell

Technically, you could use itertools.product to get a cartesian product of N sequences, and iterate over that:

 for y, x, y1, x1 in itertools.product(range(3), repeat=4):
   do_something_else()

But I don’t think that actually wins you anything readability-wise.

Answered By: Pavel Minaev

This is fairly common when looping over multidimensional spaces. My solution is:

xy_grid = [(x, y) for x in range(3) for y in range(3)]

for x, y in xy_grid:
    # do something
    for x1, y1 in xy_grid:
        # do something else
Answered By: tom10

From your code it looks like you want to perform an operation with every possible pair of points where x and y are in the range 0..2.

To do that:

for x1,y1,x2,y2 in itertools.product(range(3), repeat=4):
    do_something_with_two_points(x1,y1,2,y2)

The operation do_something_with_two_points will be called 81 times – once for every possible combination of points.

Answered By: Kenan Banks

Python iterators, and generators in particular, exist exactly to allow the nice refactoring of otherwise-complicated loops. Of course, it’s hard to get an abstraction out from a simple example, but assuming the 3 needs to be a parameter (maybe the whole range(3) should be?), and the two functions you’re calling need some parameters that are loop variables, you could refactor the code:

  for y in range(3):
    for x in range(3):
      do_something(x, y)
      for y1 in range(3):
        for x1 in range(3):
          do_something_else(x, y, x1, y1)

into, e.g.:

def nestloop(n, *funcs):
  head = funcs[0]
  tail = funcs[1:]
  for y in range(n):
    for x in range(n):
      yield head, x, y
      if tail:
        for subtup in nestloop(n, *tail):
           yield subtup[:1] + (x, y) + subtup[1:]

for funcandargs in nestloop(3, do_something, do_something_else):
  funcandargs[0](*funcandargs[1:])

The exact kind of refactoring will no doubt need to be tweaked for your exact purposes, but the general point that iterators (and usually in fact just simple generators) afford very nice refactorings of loops remains — all the looping logic goes inside the generator, and the application-level code is left with simple for loops and actual application-relevant processing of the items yielded in the for loops.

Answered By: Alex Martelli
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.