Comment sortir de plusieurs boucles?


482

Étant donné le code suivant (qui ne fonctionne pas):

while True:
    #snip: print out current state
    while True:
        ok = get_input("Is this ok? (y/n)")
        if ok.lower() == "y": break 2 #this doesn't work :(
        if ok.lower() == "n": break
    #do more processing with menus and stuff

Existe-t-il un moyen de faire fonctionner cela? Ou dois-je faire une vérification pour sortir de la boucle d'entrée, puis une autre, plus limitée, vérifier la boucle extérieure pour sortir toutes ensemble si l'utilisateur est satisfait?


87
Pourquoi Python n'a-t-il pas simplement 'break (n)' où n est le nombre de niveaux dont vous voulez sortir.
Nathan

2
C ++ est bien ici avec gotosi vous êtes niché au fond de beaucoup de boucles
Drake Johnson

Réponses:


512

Mon premier réflexe serait de refactoriser la boucle imbriquée en fonction et de l'utiliser returnpour sortir.


3
C'est une autre pensée que j'ai eue, car une fonction get_input_yn () serait utile ailleurs aussi, j'en suis sûr.
Matthew Scharley

96
convenu dans ce cas spécifique, mais dans le cas général de «J'ai des boucles imbriquées, que dois-je faire», le refactoring peut ne pas avoir de sens.
quick_dry

l'utilisation d'une exception peut être plus facile lorsque vous devez céder au lieu d'utiliser return, mais vous devriez probablement utiliser itertools.islice () dans un tel cas.
robert king

5
Il est généralement possible de refactoriser la boucle interne dans sa propre méthode, qui renvoie true pour continuer, false pour rompre la boucle externe. tandis que condition1: / sinon MyLoop2 (params): break. Une alternative consiste à définir un drapeau booléen, qui est testé aux deux niveaux. more = True / while condition1 and more: / while condition2 and more: / if stopCondition: more = False / break / ...
ToolmakerSteve

7
Je suis d'accord que s'efforcer d'utiliser returnest la bonne approche. Et le raisonnement est que, selon le Zen de Python , "plat est meilleur que imbriqué". Nous avons trois niveaux d'imbrication ici et si cela commence à gêner, il est temps de réduire l'imbrication ou au moins d'extraire toute l'imbrication dans une fonction qui lui est propre.
Lutz Prechelt du

240

Voici une autre approche courte. L'inconvénient est que vous ne pouvez rompre que la boucle externe, mais parfois c'est exactement ce que vous voulez.

for a in xrange(10):
    for b in xrange(20):
        if something(a, b):
            # Break the inner loop...
            break
    else:
        # Continue if the inner loop wasn't broken.
        continue
    # Inner loop was broken, break the outer.
    break

Ceci utilise la construction for / else expliquée à: Pourquoi python utilise 'else' après les boucles for et while?

Aperçu: il semble que la boucle externe se brise toujours. Mais si la boucle intérieure ne se rompt pas, la boucle extérieure non plus.

La continuedéclaration est la magie ici. C'est dans la clause for-else. Par définition, cela se produit s'il n'y a pas de rupture intérieure. Dans cette situation, continuecontourne proprement la coupure extérieure.


6
@eugeney Pourquoi pas? La première pause sortira de la boucle intérieure.
Navin

5
@eugeney J'ai l'impression de manquer quelque chose ici. Pouvez-vous poster un exemple?
Navin

4
@Mingliang qui peut précéder la poursuite.
Baldrickk

1
Vous avez obtenu ceci d'une vidéo de Raymond Hettinger, youtu.be/OSGv2VnC0go?t=971 , lisez les instructions "else" attachées aux boucles pour "no_break", puis cela devient plus facile à comprendre.
Ambareesh

2
C'est intelligent. :-) Cependant, pas simple. Franchement, je ne suis pas convaincu par les arguments pour garder étiqueté break ou break (n) hors de Python. Les solutions de contournement ajoutent plus de complexité.
rfportilla

148

Le PEP 3136 propose la mention pause / continuer. Guido l'a rejeté car "le code si compliqué d'exiger cette fonctionnalité est très rare". Le PEP mentionne cependant quelques solutions de contournement (telles que la technique d'exception), tandis que Guido pense que la refactorisation pour utiliser le retour sera plus simple dans la plupart des cas.


73
Bien que, refactor / returnsoit généralement la voie à suivre, j'ai vu pas mal de cas où une simple break 2déclaration concise ' ' aurait tellement de sens. De plus, refactor / returnne fonctionne pas de la même manière continue. Dans ces cas, la rupture et la poursuite numériques seraient plus faciles à suivre et moins encombrées que la refactorisation vers une fonction minuscule, levant des exceptions ou une logique alambiquée impliquant la définition d'un indicateur pour qu'il se casse à chaque niveau d'imbrication. C'est dommage que Guido l'ait rejeté.
James Haigh

10
break; breakserait bien.
PyRulez

5
@Jeyekomon Le problème est que vous n'avez pas besoin de 3 boucles imbriquées ou plus pour que ce soit un problème. 2 boucles imbriquées sont assez courantes
Jon

6
"Le code si compliqué d'exiger cette fonctionnalité est très rare". Mais si jamais vous utilisez du code aussi compliqué, le manque de boucles étiquetées le rendra encore plus compliqué, car vous devez transférer manuellement breaktoutes les boucles. Stupide.
BallpointBen

3
Apparemment, je ne peux éditer un article que pendant 5 minutes (ça fait 6). Donc, voici mon article édité: Mes 2 cents: Perl a étiqueté break (mais l'appelle 'last') et 'next' pour passer directement à la prochaine itération. Ce n'est pas du tout rare - je l'utilise tout le temps. Je suis tout nouveau sur Python et j'en ai déjà besoin. En outre, les ruptures numérotées seraient horribles pour le refactoring - mieux vaut étiqueter la boucle que vous souhaitez rompre, puis utilisez break <label> pour indiquer explicitement la boucle que vous souhaitez rompre.
John Deighan

119

Premièrement, la logique ordinaire est utile.

Si, pour une raison quelconque, les conditions de résiliation ne peuvent pas être définies, les exceptions sont un plan de secours.

class GetOutOfLoop( Exception ):
    pass

try:
    done= False
    while not done:
        isok= False
        while not (done or isok):
            ok = get_input("Is this ok? (y/n)")
            if ok in ("y", "Y") or ok in ("n", "N") : 
                done= True # probably better
                raise GetOutOfLoop
        # other stuff
except GetOutOfLoop:
    pass

Pour cet exemple spécifique, une exception peut ne pas être nécessaire.

D'un autre côté, nous avons souvent des options "Y", "N" et "Q" dans les applications en mode caractère. Pour l'option "Q", nous voulons une sortie immédiate. C'est plus exceptionnel.


4
Sérieusement, les exceptions sont extrêmement bon marché et le python idiomatique en utilise beaucoup, beaucoup. Il est également très facile de définir et de lancer des modèles personnalisés.
Gregg Lind

13
Idée intéressante. Je suis déchiré de savoir si je dois l'aimer ou le détester.
Craig McQueen

8
Cette solution serait plus utile si elle montrait les deux variantes séparément. (1) à l'aide d'un drapeau ( done). (2) levant une exception. Les fusionner en une seule solution rend tout simplement compliqué. Pour les futurs lecteurs: Soit utiliser toutes les lignes impliquant done, OU définir GetOutOfLoop(Exception)et augmenter / sauf cela.
ToolmakerSteve

4
En général, l'utilisation de blocs d'essai pour autre chose que des exceptions est très mal vue. Les blocs d'essai sont spécifiquement conçus pour la gestion des erreurs, et leur utilisation pour un flux de contrôle étrange n'est pas très bonne, stylistiquement.
nobillygreen

3
@ tommy.carstensen C'est absurde; à la fois la définition d'une nouvelle sous-classe d'exception et son élévation (comme indiqué dans la réponse) et la transmission d'un message personnalisé au Exceptionconstructeur (par exemple raise Exception('bla bla bla')) sont valides à la fois dans Python 2 et Python 3. La première est préférable dans ce cas parce que nous ne voulons pas notre exceptbloc pour intercepter toutes les exceptions, mais uniquement l'exception spéciale que nous utilisons pour quitter la boucle. Si nous faisons les choses comme vous le suggérez, puis qu'un bogue dans notre code provoque une exception inattendue, il sera traité à tort de la même manière que la fermeture délibérée de la boucle.
Mark Amery

54

J'ai tendance à convenir que la refactorisation dans une fonction est généralement la meilleure approche pour ce genre de situation, mais lorsque vous avez vraiment besoin de sortir de boucles imbriquées, voici une variante intéressante de l'approche de levée d'exceptions décrite par @ S.Lott. Il utilise l' withinstruction de Python pour rendre la levée d'exceptions un peu plus agréable. Définissez un nouveau gestionnaire de contexte (vous ne devez le faire qu'une seule fois) avec:

from contextlib import contextmanager
@contextmanager
def nested_break():
    class NestedBreakException(Exception):
        pass
    try:
        yield NestedBreakException
    except NestedBreakException:
        pass

Vous pouvez maintenant utiliser ce gestionnaire de contexte comme suit:

with nested_break() as mylabel:
    while True:
        print "current state"
        while True:
            ok = raw_input("Is this ok? (y/n)")
            if ok == "y" or ok == "Y": raise mylabel
            if ok == "n" or ok == "N": break
        print "more processing"

Avantages: (1) il est légèrement plus propre (pas de bloc try-except explicite), et (2) vous obtenez une Exceptionsous - classe personnalisée pour chaque utilisation de nested_break; pas besoin de déclarer votre propre Exceptionsous-classe à chaque fois.


40

Tout d'abord, vous pouvez également envisager de faire du processus d'obtention et de validation de l'entrée une fonction; dans cette fonction, vous pouvez simplement renvoyer la valeur si elle est correcte, et continuer à tourner dans le temps en boucle sinon. Cela évite essentiellement le problème que vous avez résolu et peut généralement être appliqué dans le cas plus général (rupture de plusieurs boucles). Si vous devez absolument conserver cette structure dans votre code et que vous ne voulez vraiment pas vous occuper des booléens de comptabilité ...

Vous pouvez également utiliser goto de la manière suivante (en utilisant un module April Fools à partir d' ici ):

#import the stuff
from goto import goto, label

while True:
    #snip: print out current state
    while True:
        ok = get_input("Is this ok? (y/n)")
        if ok == "y" or ok == "Y": goto .breakall
        if ok == "n" or ok == "N": break
    #do more processing with menus and stuff
label .breakall

Je sais, je sais, "tu n'utiliseras pas goto" et tout ça, mais ça marche bien dans des cas étranges comme celui-ci.


1
Si c'est quelque chose comme la commande COME FROM dans INTERCAL, alors rien
1800 INFORMATION

3
j'aime la blague, mais le point de débordement de pile est de promouvoir un bon code, donc je dois vous voter contre :(
Christian Oudard

13
Je pense que c'est une solution suffisamment claire et lisible pour être considérée comme un bon code, donc je vote pour. :)
JT Hurley

1
@JTHurley non ce n'est pas propre et lisible. Je veux dire, il peut sembler propre et lisible dans cet exemple, mais dans n'importe quel scénario de la vie réelle, goto crée un désordre sacré . (C'est aussi tellement anti-pythonique ...)
Alois Mahdal

2
goto obtient un mauvais représentant, tout codeur professionnel devrait être capable de le gérer correctement à mon avis.
Albert Renshaw

33

Introduisez une nouvelle variable que vous utiliserez comme «disjoncteur de boucle». Attribuez-lui d'abord quelque chose (False, 0, etc.), puis, à l'intérieur de la boucle externe, avant de vous en séparer, changez la valeur en quelque chose d'autre (True, 1, ...). Une fois la boucle terminée, vérifiez la valeur de la boucle «parent». Permettez-moi de démontrer:

breaker = False #our mighty loop exiter!
while True:
    while True:
        if conditionMet:
            #insert code here...
            breaker = True 
            break
    if breaker: # the interesting part!
        break   # <--- !

Si vous avez une boucle infinie, c'est la seule issue; pour les autres boucles, l'exécution est vraiment beaucoup plus rapide. Cela fonctionne également si vous avez plusieurs boucles imbriquées. Vous pouvez quitter tout ou quelques-uns. Possibilités infinies! J'espère que cela vous a aidé!


22

Pour sortir de plusieurs boucles imbriquées, sans refactorisation dans une fonction, utilisez une "instruction goto simulée" avec l' exception StopIteration intégrée :

try:
    for outer in range(100):
        for inner in range(100):
            if break_early():
                raise StopIteration

except StopIteration: pass

Voir cette discussion sur l'utilisation des instructions goto pour sortir des boucles imbriquées.


1
Cela semble beaucoup plus agréable que de créer votre propre classe pour gérer l'exception, et semble très propre. Y a-t-il une raison pour laquelle je ne devrais pas faire cela?
mgjk

En fait, StopIteration utilise pour les générateurs, mais je pense que normalement vous n'avez aucune exception StopIteration non capturée. Cela semble donc être une bonne solution, mais il n'y a pas d'erreur quand même à créer une nouvelle exception.
Kowalski

1
La solution la meilleure et la plus simple pour moi
Alexandre Huat

16

keeplooping=True
while keeplooping:
    #Do Stuff
    while keeplooping:
          #do some other stuff
          if finisheddoingstuff(): keeplooping=False

ou quelque chose comme ça. Vous pouvez définir une variable dans la boucle interne et la vérifier dans la boucle externe immédiatement après la sortie de la boucle interne, en la cassant si nécessaire. J'aime un peu la méthode GOTO, à condition que cela ne vous dérange pas d'utiliser un module de blague de April Fool - ce n'est pas Pythonic, mais cela a du sens.


c'est en quelque sorte le réglage du drapeau!
SIslam

Je pense que c'est une très bonne solution.
Kowalski

13

Ce n'est pas la plus jolie façon de le faire, mais à mon avis, c'est la meilleure façon.

def loop():
    while True:
    #snip: print out current state
        while True:
            ok = get_input("Is this ok? (y/n)")
            if ok == "y" or ok == "Y": return
            if ok == "n" or ok == "N": break
        #do more processing with menus and stuff

Je suis presque sûr que vous pourriez également trouver quelque chose en utilisant la récursivité, mais je ne sais pas si c'est une bonne option pour vous.


C'était la bonne solution pour moi. Mon cas d'utilisation était très différent de celui de l'OP. Je parcourais essentiellement les mêmes données deux fois pour trouver des permutations, donc je ne voulais pas séparer les deux boucles while.
Brian Peterson

9

Et pourquoi ne pas continuer à boucler si deux conditions sont vraies? Je pense que c'est une manière plus pythonique:

dejaVu = True

while dejaVu:
    while True:
        ok = raw_input("Is this ok? (y/n)")
        if ok == "y" or ok == "Y" or ok == "n" or ok == "N":
            dejaVu = False
            break

N'est-ce pas?

Bonne chance.


pourquoi pas juste while dejaVu:? Vous le définissez de toute façon.
Matthew Scharley

hé ça marche! Je pensais dans deux Trueconditions à sauter deux boucles, mais une seule suffit.
Mauro Aspé

2
@MatthewScharley Je pense que c'est pour montrer que cela fonctionne dans des boucles imbriquées.
gérer

@ MauroAspé cela ne fera pas exactement ce que demande l'OP. il exécutera toujours toute la boucle externe mais l'objectif est que si vous cassez le reste du code ne sera pas exécuté
yamm

@yamm Cela ne pourrait-il pas être résolu avec un if not dejaVu: breaken bas et quitter ainsi la boucle principale? Je pense que la solution est la plus proche de ce qui a été demandé. +1
milcak

8

Factorisez votre logique de boucle dans un itérateur qui produit les variables de boucle et retourne une fois terminé - en voici une simple qui dispose les images en lignes / colonnes jusqu'à ce que nous soyons à court d'images ou hors d'endroits pour les mettre:

def it(rows, cols, images):
    i = 0
    for r in xrange(rows):
        for c in xrange(cols):
            if i >= len(images):
                return
            yield r, c, images[i]
            i += 1 

for r, c, image in it(rows=4, cols=4, images=['a.jpg', 'b.jpg', 'c.jpg']):
    ... do something with r, c, image ...

Cela a l'avantage de diviser la logique de boucle compliquée et le traitement ...


3

Dans ce cas, comme d'autres l'ont également souligné, la décomposition fonctionnelle est la voie à suivre. Code en Python 3:

def user_confirms():
    while True:
        answer = input("Is this OK? (y/n) ").strip().lower()
        if answer in "yn":
            return answer == "y"

def main():
    while True:
        # do stuff
        if user_confirms():
            break

3

Il existe une astuce cachée dans la while ... elsestructure Python qui peut être utilisée pour simuler la double coupure sans beaucoup de changements / ajouts de code. En substance, si la whilecondition est fausse, le elseblocage est déclenché. Ni exception, continueni breakdéclenchement du elseblocage. Pour plus d'informations, voir les réponses à la " clause Else sur l'instruction Python while " ou la documentation Python sur while (v2.7) .

while True:
    #snip: print out current state
    ok = ""
    while ok != "y" and ok != "n":
        ok = get_input("Is this ok? (y/n)")
        if ok == "n" or ok == "N":
            break    # Breaks out of inner loop, skipping else

    else:
        break        # Breaks out of outer loop

    #do more processing with menus and stuff

Le seul inconvénient est que vous devez déplacer la double condition de rupture dans la whilecondition (ou ajouter une variable d'indicateur). Il existe également des variations pour la forboucle, où le elsebloc est déclenché après l'achèvement de la boucle.


Cela ne semble pas remplir l'exigence de doubles pauses. Fonctionne pour le problème donné, mais pas pour la question réelle.
Dakkaron

@Dakkaron Êtes-vous sûr d'avoir bien compris le code? Le code résout en effet la question des PO et se casse de manière similaire à la demande. Cependant, il ne sort pas de plusieurs boucles, mais utilise la clause else pour remplacer la nécessité de doubler la pause.
holroy

D'après ma compréhension, la question était How to break out of multiple loops in Python?et la réponse aurait dû être "Cela ne fonctionne pas, essayez autre chose". Je sais que cela corrige l'exemple exact donné de l'OP, mais ne répond pas à leur question.
Dakkaron

@Dakkaron, Voir l'énoncé du problème sous le code, et à mon avis, il répond en effet à la question des PO.
holroy

2

Une autre façon de réduire votre itération à une boucle à un seul niveau serait d'utiliser des générateurs comme spécifié également dans la référence python

for i, j in ((i, j) for i in A for j in B):
    print(i , j)
    if (some_condition):
        break

Vous pouvez l'adapter à n'importe quel nombre de niveaux pour la boucle

L'inconvénient est que vous ne pouvez plus casser un seul niveau. C'est tout ou rien.

Un autre inconvénient est qu'il ne fonctionne pas avec une boucle while. À l'origine, je voulais publier cette réponse sur Python - "sortir" de toutes les boucles, mais malheureusement, c'est fermé en double de celui-ci


1
Cela fonctionne aussi pour les boucles while, il vous suffit d'écrire votre générateur en def (avec yield), pas en compréhension.
Veky

Oui, un haut-parleur d'un PyCon prétend ici que même la réponse acceptée de @ RobertRossney n'est pas vraiment Pythonic, mais un générateur est la bonne façon de rompre plusieurs boucles. (Je recommanderais de regarder toute la vidéo!)
Post169

2

Ma raison d'être ici est que j'avais une boucle externe et une boucle interne comme ceci:

for x in array:
  for y in dont_use_these_values:
    if x.value==y:
      array.remove(x)  # fixed, was array.pop(x) in my original answer
      continue

  do some other stuff with x

Comme vous pouvez le voir, il ne passera pas au x suivant, mais passera au y suivant à la place.

ce que j'ai trouvé pour résoudre ce problème était simplement de parcourir le tableau deux fois à la place:

for x in array:
  for y in dont_use_these_values:
    if x.value==y:
      array.remove(x)  # fixed, was array.pop(x) in my original answer
      continue

for x in array:
  do some other stuff with x

Je sais que c'était un cas spécifique de la question d'OP, mais je le poste dans l'espoir que cela aidera quelqu'un à penser son problème différemment tout en gardant les choses simples.


Ce n'est probablement pas Python. Quel est le type de tableau? Liste probablement, mais que contient-elle? Même s'il contient des ints, array.pop (x) ne fera probablement pas ce que vous voulez.
Veky

C'est un bon point. Je ne trouve pas le code que j'ai référencé. Pour tous ceux qui lisent ceci, array.pop (i) "Supprime l'élément avec l'index i du tableau et le renvoie." selon la documentation de python. Il faudrait donc obtenir l'index de l'élément x dans le tableau pour que ce code fonctionne comme prévu. Il y a aussi la fonction array.remove (x) qui ferait ce qui est attendu. Je modifierai ma réponse ci-dessus pour corriger cette erreur. Cela suppose que le deuxième tableau ne contient aucun doublon, car array.remove (x) supprimera uniquement la première instance de x trouvée.
Nathan Garabedian

D'accord, je comprends. Dans ce cas, simplement utiliser breakau lieu de continueferait ce que vous voulez, non? :-)
Veky

Oui, pour plus d'efficacité et de clarté, vous voudrez probablement utiliser break au lieu de continuer dans ces exemples. :)
Nathan Garabedian

2

Essayez d'utiliser un générateur infini.

from itertools import repeat
inputs = (get_input("Is this ok? (y/n)") for _ in repeat(None))
response = (i.lower()=="y" for i in inputs if i.lower() in ("y", "n"))

while True:
    #snip: print out current state
    if next(response):
        break
    #do more processing with menus and stuff

2

En utilisant une fonction:

def myloop():
    for i in range(1,6,1):  # 1st loop
        print('i:',i)
        for j in range(1,11,2):  # 2nd loop
            print('   i, j:' ,i, j)
            for k in range(1,21,4):  # 3rd loop
                print('      i,j,k:', i,j,k)
                if i%3==0 and j%3==0 and k%3==0:
                    return  # getting out of all loops

myloop()

Essayez d'exécuter les codes ci-dessus en les commentant returnégalement.

Sans utiliser aucune fonction:

done = False
for i in range(1,6,1):  # 1st loop
    print('i:', i)
    for j in range(1,11,2):  # 2nd loop
        print('   i, j:' ,i, j)
        for k in range(1,21,4):  # 3rd loop
            print('      i,j,k:', i,j,k)
            if i%3==0 and j%3==0 and k%3==0:
                done = True
                break  # breaking from 3rd loop
        if done: break # breaking from 2nd loop
    if done: break     # breaking from 1st loop

Maintenant, exécutez les codes ci-dessus tels quels, puis essayez de les exécuter en commentant chaque ligne contenant breakune à la fois à partir du bas.


2

Un moyen facile de transformer plusieurs boucles en une seule boucle cassable consiste à utiliser numpy.ndindex

for i in range(n):
  for j in range(n):
    val = x[i, j]
    break # still inside the outer loop!

for i, j in np.ndindex(n, n):
  val = x[i, j]
  break # you left the only loop there was!

Vous devez indexer dans vos objets, au lieu de pouvoir parcourir les valeurs explicitement, mais au moins dans les cas simples, cela semble être environ 2-20 fois plus simple que la plupart des réponses suggérées.


2
# this version uses a level counter to choose how far to break out

break_levels = 0
while True:
    # snip: print out current state
    while True:
        ok = get_input("Is this ok? (y/n)")
        if ok == "y" or ok == "Y":
            break_levels = 1        # how far nested, excluding this break
            break
        if ok == "n" or ok == "N":
            break                   # normal break
    if break_levels:
        break_levels -= 1
        break                       # pop another level
if break_levels:
    break_levels -= 1
    break

# ...and so on

1

probablement peu de truc comme ci-dessous fera sinon préfère refactoriel en fonction

ajout d'une variable break_level pour contrôler la condition de la boucle while

break_level = 0
# while break_level < 3: # if we have another level of nested loop here
while break_level < 2:
    #snip: print out current state
    while break_level < 1:
        ok = get_input("Is this ok? (y/n)")
        if ok == "y" or ok == "Y": break_level = 2 # break 2 level
        if ok == "n" or ok == "N": break_level = 1 # break 1 level

1

Vous pouvez définir une variable (par exemple break_statement ), puis la changer en une valeur différente lorsque la condition à deux ruptures se produit et l'utiliser dans l'instruction if pour rompre également à partir de la deuxième boucle.

while True:
    break_statement=0
    while True:
        ok = raw_input("Is this ok? (y/n)")
        if ok == "n" or ok == "N": 
            break
        if ok == "y" or ok == "Y": 
            break_statement=1
            break
    if break_statement==1:
        break

Bon point, cependant, dans chacun des niveaux supérieurs à notre niveau d'intérêt interne, nous aurions besoin de scanner cette variable. Se sent vraiment mal que la langue ne dispose pas d'une instruction GoTo, en termes de performances.
Anatoly Alekseev

1

Je voudrais vous rappeler que les fonctions en Python peuvent être créées en plein milieu du code et peuvent accéder aux variables environnantes de manière transparente pour la lecture et avec nonlocalouglobal déclaration pour l'écriture.

Vous pouvez donc utiliser une fonction comme une "structure de contrôle cassable", définissant un endroit où vous souhaitez revenir:

def is_prime(number):

    foo = bar = number

    def return_here():
        nonlocal foo, bar
        init_bar = bar
        while foo > 0:
            bar = init_bar
            while bar >= foo:
                if foo*bar == number:
                    return
                bar -= 1
            foo -= 1

    return_here()

    if foo == 1:
        print(number, 'is prime')
    else:
        print(number, '=', bar, '*', foo)

>>> is_prime(67)
67 is prime
>>> is_prime(117)
117 = 13 * 9
>>> is_prime(16)
16 = 4 * 4

1

Solutions de 2 façons

Avec un exemple: ces deux matrices sont-elles égales / identiques?
matrice1 et matrice2 sont de même taille, n, 2 matrices dimensionnelles.

Première solution , sans fonction

same_matrices = True
inner_loop_broken_once = False
n = len(matrix1)

for i in range(n):
    for j in range(n):

        if matrix1[i][j] != matrix2[i][j]:
            same_matrices = False
            inner_loop_broken_once = True
            break

    if inner_loop_broken_once:
        break

Deuxième solution , avec une fonction
C'est la solution finale pour mon cas

def are_two_matrices_the_same (matrix1, matrix2):
    n = len(matrix1)
    for i in range(n):
        for j in range(n):
            if matrix1[i][j] != matrix2[i][j]:
                return False
    return True

Bonne journée!


1
# this version breaks up to a certain label

break_label = None
while True:
    # snip: print out current state
    while True:
        ok = get_input("Is this ok? (y/n)")
        if ok == "y" or ok == "Y":
            break_label = "outer"   # specify label to break to
            break
        if ok == "n" or ok == "N":
            break
    if break_label:
        if break_label != "inner":
            break                   # propagate up
        break_label = None          # we have arrived!
if break_label:
    if break_label != "outer":
        break                       # propagate up
    break_label = None              # we have arrived!

#do more processing with menus and stuff

0

Espérons que cela aide:

x = True
y = True
while x == True:
    while y == True:
         ok = get_input("Is this ok? (y/n)") 
         if ok == "y" or ok == "Y":
             x,y = False,False #breaks from both loops
         if ok == "n" or ok == "N": 
             break #breaks from just one

0

Voici une implémentation qui semble fonctionner:

break_ = False
for i in range(10):
    if break_:
        break
    for j in range(10):
        if j == 3:
            break_ = True
            break
        else:
            print(i, j)

Le seul inconvénient est que vous devez définir break_avant les boucles.


0

Il n'y a aucun moyen de le faire à partir d'un niveau de langue. Certaines langues ont un goto, d'autres ont une pause qui prend un argument, pas python.

Les meilleures options sont:

  1. Définissez un indicateur qui est vérifié par la boucle externe ou définissez la condition des boucles externes.

  2. Mettez la boucle dans une fonction et utilisez return pour sortir de toutes les boucles à la fois.

  3. Reformulez votre logique.

Le mérite revient à Vivek Nagarajan, programmeur depuis 1987


Utilisation de la fonction

def doMywork(data):
    for i in data:
       for e in i:
         return 

Utilisation du drapeau

is_break = False
for i in data:
   if is_break:
      break # outer loop break
   for e in i:
      is_break = True
      break # inner loop break

-3

Similaire à la précédente, mais plus compact. (Les booléens ne sont que des nombres)

breaker = False #our mighty loop exiter!
while True:
    while True:
        ok = get_input("Is this ok? (y/n)")
        breaker+= (ok.lower() == "y")
        break

    if breaker: # the interesting part!
        break   # <--- !

2
Cela semble assez moche et rend le code plus difficile à comprendre, par rapport au précédent. De plus, c'est faux. Il ne vérifie pas réellement si l'entrée est acceptable et se casse après 1 boucle.
Eric

-3

Puisque cette question est devenue une question standard pour pénétrer dans une boucle particulière, je voudrais donner ma réponse avec un exemple en utilisant Exception.

Bien qu'il n'existe aucune étiquette nommée rupture de boucle dans une construction en boucle multipale, nous pouvons utiliser des exceptions définies par l'utilisateur pour pénétrer dans une boucle particulière de notre choix. Prenons l'exemple suivant où imprimons tous les nombres jusqu'à 4 chiffres dans le système de numérotation base-6:

class BreakLoop(Exception):
    def __init__(self, counter):
        Exception.__init__(self, 'Exception 1')
        self.counter = counter

for counter1 in range(6):   # Make it 1000
    try:
        thousand = counter1 * 1000
        for counter2 in range(6):  # Make it 100
            try:
                hundred = counter2 * 100
                for counter3 in range(6): # Make it 10
                    try:
                        ten = counter3 * 10
                        for counter4 in range(6):
                            try:
                                unit = counter4
                                value = thousand + hundred + ten + unit
                                if unit == 4 :
                                    raise BreakLoop(4) # Don't break from loop
                                if ten == 30: 
                                    raise BreakLoop(3) # Break into loop 3
                                if hundred == 500:
                                    raise BreakLoop(2) # Break into loop 2
                                if thousand == 2000:
                                    raise BreakLoop(1) # Break into loop 1

                                print('{:04d}'.format(value))
                            except BreakLoop as bl:
                                if bl.counter != 4:
                                    raise bl
                    except BreakLoop as bl:
                        if bl.counter != 3:
                            raise bl
            except BreakLoop as bl:
                if bl.counter != 2:
                    raise bl
    except BreakLoop as bl:
        pass

Lorsque nous imprimons la sortie, nous n'obtiendrons jamais de valeur dont la place unitaire est à 4. Dans ce cas, nous ne nous séparons d'aucune boucle car elle BreakLoop(4)est élevée et prise dans la même boucle. De même, chaque fois que dix places ont 3, nous entrons dans la troisième boucle en utilisant BreakLoop(3). Chaque fois que cent lieux ont 5, nous entrons dans la deuxième boucle en utilisant BreakLoop(2)et chaque fois que les mille lieux en ont 2, nous entrons dans la première boucle en utilisantBreakLoop(1) .

En bref, augmentez votre exception (intégrée ou définie par l'utilisateur) dans les boucles internes et interceptez-la dans la boucle à partir de laquelle vous souhaitez reprendre votre contrôle. Si vous souhaitez interrompre toutes les boucles, interceptez l'exception en dehors de toutes les boucles. (Je n'ai pas montré ce cas dans l'exemple).

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.