2009-08-14 8 views
33

Este problema me afecta desde hace un tiempo. ¿Existe alguna manera más fácil de escribir anidados for bucles en python? Por ejemplo, si el código fue algo como esto:En python, ¿hay alguna manera más fácil de escribir 6 bucles anidados?

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 sangras en lugar de usar 2 espacios, como yo, puede llegar a ser un problema.

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

+0

[Ver también.] (Http://stackoverflow.com/a/533917/1430829) –

Respuesta

53

Si eres con frecuencia iterando sobre un producto cartesiano como en su ejemplo, es posible que desee investigar Python 2.6's itertools.product - o escriba el suyo si está en un Python anterior.

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

Si anidado en un bucle así: ' 'para y en el rango (3): do_something1() para x en el rango (3): do_something()' '' cómo puedo usar el producto –

+0

@ ErtuğrulAltınboğa Sin indentación es imposible decir lo que está tratando de hacer Sugiero hacer una nueva pregunta de StackOverflow en lugar de tratar de obtener ayuda en los comentarios. –

9

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

Otra técnica en Python es usar list comprehensions donde sea posible, en lugar de un bucle.

2

Mi argumento personal sería que es muy probable haciendo algo mal si tiene 6 bucles anidados ...

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

+2

Aquí está el caso de uso en el que tuve por última vez tantos bucles anidados: Imagine que aplica un filtro gaussiano 3D "borroso" a un 3D conjunto de datos; tiene sumatorias en las tres direcciones para el filtro (3 bucles) e itera sobre cada punto en tres direcciones para aplicarlo (3 bucles más). Ah, y los datos que estaba filtrando eran velocidades, por lo que hubo tres componentes para iterar (1 ciclo final). –

6

Suponiendo que cada bucle tiene algún tipo de significado independiente, romper hacia fuera 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. 

que tal vez podría haber elegido mejores nombres. 8-)

+3

Aww, no gatitos :( –

+4

Haría las leonas si fuera un león. – Skurmedel

+3

Bueno, esa es una forma de conseguir gatitos ... Aunque, son un poco más grandes de lo que tenía en mente. –

1

De esa manera se ve bastante sencillo y fácil. ¿Estás diciendo que quieres generalizar a varias capas de bucles ... puedes dar un ejemplo de la vida real?

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

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

for x,y in generate_params(3): 
    do_something() 
4

Técnicamente, se podría utilizar itertools.product para obtener un producto cartesiano de N secuencias, y pasar sobre ese:

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

Pero no creo que realmente se gana nada legibilidad se refiere.

13

Esto es bastante común cuando se repite 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 
+0

Además, si tiene una gran cantidad de elementos para recorrer, podría ser mejor usar una expresión de generador, es decir, 'xy_grid = ((x, y) para x en el rango (30000) para y en el rango (30000)) '. – Garrett

2

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

de hacerlo:

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 será llamado 81 veces - una vez para cada combinación posible de puntos.

3

Los iteradores de Python, y los generadores en particular, existen exactamente para permitir la agradable refactorización de bucles que de otro modo serían complicados. Por supuesto, es difícil extraer una abstracción de un ejemplo simple, pero suponiendo que el 3 necesita ser un parámetro (¿quizás debería ser el total range(3)?), Y las dos funciones a las que llama necesitan algunos parámetros que sean variables de bucle, usted podría refactorizar el código:

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

necesitarán sin duda el tipo exacto de refactorización que ser ajustado para sus propósitos exactos, pero el punto general de que los iteradores (y por lo general de hecho solo generadores simples) permiten refactorizaciones muy agradables de bucles permanece - todos los logi de bucle c va dentro del generador, y el código del nivel de la aplicación se deja con los bucles for simples y el procesamiento real relevante de la aplicación de los elementos producidos en los bucles for.

Cuestiones relacionadas