Étant donné le code suivant (cela ne fonctionne pas):
while True:
#snip: print out current state
while True:
ok = get_input("Is this ok? (y/n)")
if ok == "y" or ok == "Y": break 2 #this doesn't work :(
if ok == "n" or ok == "N": break
#do more processing with menus and stuff
Y at-il un moyen de faire ce travail? Ou dois-je faire un contrôle pour sortir de la boucle d'entrée, puis un autre, plus limité, vérifier dans la boucle extérieure pour sortir tous ensemble si l'utilisateur est satisfait?
Edit-FYI: get_input est une fonction courte que j'ai écrite et qui permet d'afficher une invite et des valeurs par défaut ainsi que tout ce qui est fantaisiste et qui renvoie stdin.readline().strip()
Mon premier instinct serait de refactoriser la boucle imbriquée dans une fonction et d’utiliser return
pour sortir.
Voici une autre approche qui est courte. L'inconvénient est que vous ne pouvez que casser 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-t-il 'else' après les boucles for et while?
Information clé: Il ne semble que comme si la boucle externe se cassait toujours. Mais si la boucle interne ne se rompt pas, la boucle externe ne le sera pas non plus.
La déclaration continue
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, continue
contourne nettement la rupture externe.
PEP 3136 propose la mention "pause/continue". Guido l'a rejetée car "le code si compliqué à exiger cette fonctionnalité est très rare". Le PEP mentionne cependant certaines solutions de contournement (telles que la technique des exceptions), tandis que Guido estime que le refactoring pour utiliser le retour sera plus simple dans la plupart des cas.
Premièrement, la logique ordinaire est utile.
Si, pour une raison quelconque, les conditions de terminaison ne peuvent pas être définies, les exceptions constituent 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'autre part, nous avons souvent les 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.
J'ai tendance à être d'accord sur le fait que refactoriser une fonction est généralement la meilleure approche pour ce genre de situation, mais lorsque vous avez réellement besoin de sortir de boucles imbriquées , voici une variante intéressante de l'approche d'exception décrite par @ S.Lott. Il utilise l'instruction with
de Python pour rendre l'apparence de l'exception 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 sous-classe Exception
construite sur mesure pour chaque utilisation de nested_break
; pas besoin de déclarer votre propre sous-classe Exception
à chaque fois.
Tout d'abord, vous pouvez également envisager de transformer le processus d'obtention et de validation de l'entrée en une fonction; dans cette fonction, vous pouvez simplement renvoyer la valeur si elle est correcte et continuer à tourner dans la boucle while 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 comptables ...
Vous pouvez aussi utiliser goto de la manière suivante (en utilisant un module April Fools de 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 de goto" et tout ça, mais ça marche bien dans des cas aussi étranges que celui-ci.
Introduisez une nouvelle variable que vous utiliserez comme "interrupteur de boucle". Attribuez-lui d’abord quelque chose (False, 0, etc.), puis, dans la boucle externe, avant de vous en séparer, modifiez la valeur en autre chose (True, 1, ...). Une fois la boucle terminée, la boucle «parent» vérifie cette valeur. 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 le seul moyen de sortir; pour d'autres boucles, l'exécution est vraiment beaucoup plus rapide. Cela fonctionne également si vous avez plusieurs boucles imbriquées. Vous pouvez sortir tous ou juste quelques uns. Des possibilités infinies! J'espère que cela a aidé!
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, le cas échéant éventuellement en rupture. J'aime un peu la méthode GOTO, à condition que l'utilisation du module de plaisanterie d'un poisson d'avril ne vous dérange pas - ce n'est pas Pythonic, mais c'est logique.
Ce n'est pas la meilleure façon de le faire, mais à mon avis, c'est la meilleure.
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 sûr que vous pouvez également trouver une solution en utilisant la récursivité, mais je ne sais pas si c'est une bonne option pour vous.
Pour sortir de plusieurs boucles imbriquées, sans refactoring 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.
Intégrez votre logique de boucle dans un itérateur qui renvoie les variables de boucle et les renvoie une fois l'opération terminée. En voici un simple: il présente les images en rangées/colonnes jusqu'à ce que nous ayons épuisé les images ou les placent:
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 complexe et le traitement des données ...
Et pourquoi ne pas continuer à boucler si deux conditions sont vraies? Je pense que cela est 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.
Dans ce cas, comme l'ont également souligné d'autres personnes, la décomposition fonctionnelle est la solution. 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
Il y a une astuce cachée dans la structure Python while ... else
qui peut être utilisée pour simuler la double coupure sans beaucoup de changements/ajouts de code. En substance, si la condition while
est false, le bloc else
est déclenché. Aucune exception, continue
ou break
ne déclenche le bloc else
. Pour plus d'informations, voir les réponses à la clause " Else sur Python tant que l'instruction ", 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 condition de rupture double dans la condition while
(ou ajouter une variable de drapeau). Il en existe également des variantes pour la boucle for
, où le bloc else
est déclenché après la fin de la boucle.
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 le paramètre python reference
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 à un nombre quelconque de niveaux pour la boucle
L'inconvénient est que vous ne pouvez plus briser un seul niveau. C'est tout ou rien.
Un autre inconvénient est que cela ne fonctionne pas avec une boucle while. À l’origine, je voulais publier cette réponse sur Python - «rompre» toutes les boucles mais malheureusement, cette copie est fermée.
Ma raison de venir ici est que j'avais une boucle externe et une boucle interne comme celle-ci:
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 constater, cela n'ira pas réellement au prochain x, mais ira au prochain y à 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 particulier de la question d’OP, mais je l’affiche dans l’espoir que cela aidera une personne à penser son problème différemment tout en gardant les choses simples.
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 commentant la return
.
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, lancez les codes ci-dessus tels quels, puis essayez-les en commentant chaque ligne contenant break
une par une à partir du bas.
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
probablement petite astuce comme ci-dessous fera sinon préférer refactorial dans la 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
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
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
Vous pouvez définir une variable (par exemple break_statement ), puis la remplacer par une valeur différente lorsqu’une condition de rupture est définie et l’utiliser dans l’instruction if pour interrompre également 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
Un moyen simple de transformer plusieurs boucles en une seule boucle sécable 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, par opposition à pouvoir parcourir les valeurs de manière explicite, mais au moins dans les cas simples, il semble être environ 2 à 20 fois plus simple que la plupart des réponses suggérées.
Je voudrais vous rappeler que des fonctions en Python peuvent être créées au milieu du code et peuvent accéder aux variables environnantes de manière transparente pour la lecture et avec la déclaration nonlocal
ou global
pour l'écriture.
Vous pouvez donc utiliser une fonction en tant que "structure de contrôle sécable", définissant un lieu vers lequel 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
Avec un exemple: Ces deux matrices sont-elles égales/identiques?
matrix1 et matrix2 ont la 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
Ceci 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!
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