En Python, ¿hay una forma más fácil de escribir 6 nesteds para bucles?

Este problema ha estado llegando a mí por un tiempo ahora. ¿Hay una forma más fácil de escribir nesteds for bucles en python? Por ejemplo, si mi código fuera algo como esto:

  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() 

¿Habría una manera más fácil de hacer esto? Sé que este código funciona, pero cuando se sangra en lugar de usar 2 espacios, como yo, puede llegar a ser un problema.

Oh, en el ejemplo solo había 4 nesteds for bucles para facilitar las cosas.

Si está iterando con frecuencia sobre un producto cartesiano como en su ejemplo, es posible que desee investigar el producto de Python 2.6 en itertools.product , o escribir el suyo propio si está en una versión anterior de 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() 

Esto es bastante común cuando se realiza un bucle en espacios multidimensionales. Mi solución es:

 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 

Cuando me enfrente a ese tipo de lógica de progtwig, probablemente dividiría la secuencia de bucles en dos o más funciones separadas.

Otra técnica en Python es usar listas de comprensión cuando sea posible, en lugar de un bucle.

Suponiendo que cada bucle tiene algún tipo de significado independiente, divídalos en funciones con nombre:

 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. 

Tal vez podría haber elegido mejores nombres. 😎

Técnicamente, podría usar itertools.product para obtener un producto cartesiano de secuencias N, e iterar sobre eso:

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

Pero no creo que eso realmente te gane nada en cuanto a la legibilidad.

Los iteradores de Python, y los generadores en particular, existen exactamente para permitir la refactorización agradable de bucles por lo demás complicados. Por supuesto, es difícil obtener una abstracción de un ejemplo simple, pero suponiendo que el 3 debe ser un parámetro (¿tal vez todo el range(3) debería ser?), Y las dos funciones que está llamando necesitan algunos parámetros que son variables de bucle, podria refactorizar el codigo:

  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) 

en, por ejemplo:

 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:]) 

El tipo exacto de refactorización sin duda tendrá que ser ajustado para sus propósitos exactos, pero el punto general de que los iteradores (y, en realidad, solo los generadores simples) proporcionan refacturaciones muy bonitas de los bucles sigue siendo la lógica del bucle. y el código de nivel de aplicación se deja simple for bucles y el procesamiento real relevante for la aplicación de los elementos producidos en los bucles for.

Mi argumento personal sería que probablemente esté haciendo algo mal si tiene 6 bucles nesteds …

Dicho esto, la descomposición funcional es lo que estás buscando. Refactorice para que algunos de los bucles ocurran en llamadas a funciones separadas, luego llame a esas funciones.

Desde su código parece que desea realizar una operación con cada par de puntos posibles donde x e y están en el rango 0..2.

Para hacer eso:

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

La operación do_something_with_two_points se llamará 81 veces, una vez por cada combinación posible de puntos.

¿Has mirado en la lista de comprensión?

Algo como:

 [do_something() for x in range(3) for y in range(3)] 

De esa manera se ve bastante sencillo y fácil. ¿Está diciendo que quiere generalizar a múltiples capas de bucles … puede dar un ejemplo de la vida real?

Otra opción que podría pensar sería utilizar una función para generar los parámetros y luego aplicarlos en un bucle

 def generate_params(n): return itertools.product(range(n), range(n)) for x,y in generate_params(3): do_something() 

También puedes usar la función map () .