web-dev-qa-db-fra.com

Dans python existe-t-il un moyen plus simple d'écrire 6 boucles for imbriquées?)

Ce problème me touche depuis un moment maintenant. Existe-t-il un moyen plus simple d'écrire des boucles for imbriquées en python? Par exemple, si mon code ressemblait à ceci:

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

y aurait-il un moyen plus simple de le faire? Je sais que ce code fonctionne mais lorsque vous indentez au lieu d'utiliser 2 espaces, comme moi, cela peut devenir un problème.

Oh, dans l'exemple, il n'y avait que 4 boucles for imbriquées pour faciliter les choses.

38
saleh

Si vous itérez fréquemment sur un produit cartésien comme dans votre exemple, vous voudrez peut-être étudier itertools.product de Python 2.6 - ou écrire le vôtre si vous êtes dans un Python antérieur.

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()
58
Alice Purcell

Ceci est assez courant lors d'une boucle sur des espaces multidimensionnels. Ma solution est:

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
14
tom10

Face à ce type de logique de programme, je diviserais probablement la séquence de boucles en deux ou plusieurs fonctions distinctes.

Une autre technique dans Python est d'utiliser list comprehensions lorsque c'est possible, au lieu d'une boucle.

10
Greg Hewgill

En supposant que chaque boucle a une sorte de signification indépendante, divisez-les en fonctions nommées:

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.

J'aurais peut-être pu choisir de meilleurs noms. 8-)

8
RichieHindle

Techniquement, vous pouvez utiliser itertools.product pour obtenir un produit cartésien de N séquences, et itérer dessus:

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

Mais je ne pense pas que cela vous rapporte quoi que ce soit en termes de lisibilité.

5
Pavel Minaev

Les itérateurs Python, et les générateurs en particulier, existent exactement pour permettre la refactorisation Nice de boucles autrement compliquées. Bien sûr, il est difficile d'obtenir une abstraction à partir d'un exemple simple, mais en supposant que 3 Doit être un paramètre (peut-être que l'ensemble range(3) devrait l'être?), Et les deux fonctions que vous Appelez besoin de certains paramètres qui sont des variables de boucle, vous pouvez refactoriser le 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)

dans, par exemple:

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

Le type exact de refactoring devra sans aucun doute être modifié pour vos besoins précis, mais le point général selon lequel les itérateurs (et généralement en fait de simples générateurs) permettent de très beaux refactorisations de boucles reste - toute la logique de boucle va à l'intérieur du générateur, et le code au niveau de l'application se retrouve avec de simples boucles for et un traitement réel pertinent pour l'application des éléments générés dans les boucles for.

4
Alex Martelli

Mon argument personnel serait que vous faites probablement quelque chose de mal si vous avez 6 boucles imbriquées ...

Cela dit, la décomposition fonctionnelle est ce que vous recherchez. Refactoriser afin que certaines des boucles se produisent dans des appels de fonction séparés, puis appelez ces fonctions.

3
Matthew Scharley

D'après votre code, il semble que vous souhaitiez effectuer une opération avec toutes les paires de points possibles où x et y sont compris entre 0 et 2.

Pour faire ça:

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

L'opération do_something_with_two_points sera appelé 81 fois - une fois pour chaque combinaison de points possible.

3
Triptych

Avez-vous examiné Compréhensions de la liste?

Quelque chose comme:

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

De cette façon, cela semble assez simple et simple. Êtes-vous en train de dire que vous souhaitez généraliser à plusieurs couches de boucles ... pouvez-vous donner un exemple réel?

Une autre option à laquelle je pourrais penser serait d'utiliser une fonction pour générer les paramètres, puis de les appliquer simplement dans une boucle

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

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

vous pouvez également utiliser map ()function

2
jle