webentwicklung-frage-antwort-db.com.de

Wie kann man in Python aus mehreren Schleifen ausbrechen?

Gegeben der folgende Code (der nicht funktioniert):

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

Gibt es eine Möglichkeit, dies zu erreichen? Oder muss ich eine Überprüfung durchführen, um die Eingabeschleife zu durchbrechen, dann eine andere, begrenztere, die äußere Schleife, um alle zu durchbrechen, wenn der Benutzer zufrieden ist?

Edit-FYI: get_input ist eine kurze Funktion, die ich geschrieben habe und die Anzeige von Eingabeaufforderungen und Standardwerten sowie alle diese Phantasie und Rückgabewerte unterstützt. stdin.readline().strip()

360

Mein erster Instinkt wäre, die verschachtelte Schleife in eine Funktion umzuwandeln und return zu verwenden. 

402
Robert Rossney

Hier ist ein weiterer Ansatz, der kurz ist. Der Nachteil ist, dass Sie nur die äußere Schleife durchbrechen können, aber manchmal ist es genau das, was Sie wollen.

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

Hierfür wird das Konstrukt for/else verwendet, das unter folgender Adresse erläutert wird: Warum verwendet python 'else' nach for- und while-Schleifen?

Key Insight: Es scheint nur , als ob die äußere Schleife immer bricht. Aber wenn die innere Schleife nicht reißt, reißt auch die äußere Schleife nicht.

Die continue -Anweisung ist hier die Magie. Es ist in der For-else-Klausel. Per Definition das passiert, wenn es keinen inneren Bruch gibt. In dieser Situation umgeht continue den äußeren Bruch sauber.

173
yak

PEP 3136 schlägt die Bezeichnung break/continue vor. Guido lehnte es ab weil "Code, der für diese Funktion so kompliziert ist, sehr selten ist". Das PEP erwähnt jedoch einige Problemumgehungen (z. B. die Ausnahmetechnik), während Guido der Meinung ist, dass das Refactoring für die Verwendung von return in den meisten Fällen einfacher ist.

136
John Fouhy

Erstens ist gewöhnliche Logik hilfreich. 

Wenn aus irgendeinem Grund die Beendigungsbedingungen nicht herausgearbeitet werden können, handelt es sich bei den Ausnahmen um einen Rückfallplan. 

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

Für dieses spezielle Beispiel ist möglicherweise keine Ausnahme erforderlich. 

Andererseits haben wir in den Zeichenmodusanwendungen oft die Optionen "Y", "N" und "Q". Für die Option "Q" möchten wir sofort beenden. Das ist außergewöhnlicher.

99
S.Lott

Ich stimme eher zu, dass Refactoring in einer Funktion normalerweise der beste Ansatz für diese Art von Situation ist, aber für den Fall, dass Sie wirklich aus verschachtelten Schleifen ausbrechen müssen, ist hier eine interessante Variante des Ansatzes zum Auslösen von Ausnahmen das hat @ s.lott beschrieben. Es verwendet die with -Anweisung von Python, um die Ausnahmebedingung ein bisschen besser aussehen zu lassen. Definieren Sie einen neuen Kontextmanager (Sie müssen dies nur einmal tun) mit:

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

Jetzt können Sie diesen Kontextmanager wie folgt verwenden:

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"

Vorteile: (1) Es ist etwas sauberer (kein expliziter try-except-Block), und (2) Sie erhalten eine benutzerdefinierte Exception -Unterklasse für jede Verwendung von nested_break; Sie müssen nicht jedes Mal Ihre eigene Exception -Unterklasse deklarieren.

49
Mark Dickinson

Erstens können Sie auch erwägen, den Vorgang des Abrufs und der Überprüfung der Eingabe als Funktion zu definieren. Innerhalb dieser Funktion können Sie den Wert einfach zurückgeben, wenn er korrekt ist, und sich in der while -Schleife drehen, wenn dies nicht der Fall ist. Dies beseitigt im Wesentlichen das Problem, das Sie gelöst haben, und kann normalerweise im allgemeineren Fall angewendet werden (Ausbruch mehrerer Schleifen). Wenn Sie diese Struktur unbedingt in Ihrem Code beibehalten müssen und sich wirklich nicht mit buchhalterischen Booleschen befassen wollen ...

Sie können goto auch folgendermaßen verwenden (mit einem Aprilscherz-Modul aus hier ):

#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

Ich weiß, ich weiß, "Du sollst goto nicht verwenden" und all das, aber es funktioniert gut in so seltsamen Fällen.

36
Matt J

Führen Sie eine neue Variable ein, die Sie als "Schleifenbrecher" verwenden. Weisen Sie ihm zuerst etwas zu (False, 0 usw.), und ändern Sie dann innerhalb der äußeren Schleife den Wert in etwas anderes (True, 1, ...). Sobald die Schleife beendet ist, prüfen Sie die 'Eltern'-Schleife auf diesen Wert. Lass mich demonstrieren:

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

Wenn Sie eine Endlosschleife haben, ist dies der einzige Ausweg. Für andere Loops ist die Ausführung wirklich viel schneller. Dies funktioniert auch, wenn Sie viele verschachtelte Schleifen haben. Sie können alle oder nur wenige beenden. Endlose Möglichkeiten! Hoffe das hat geholfen!

25
krvolok

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

oder sowas ähnliches. Sie können eine Variable in der inneren Schleife festlegen und diese unmittelbar nach dem Verlassen der inneren Schleife in der äußeren Schleife überprüfen. Ich mag die GOTO-Methode, vorausgesetzt, es macht Ihnen nichts aus, ein Witzmodul von April Fool zu verwenden - es ist nicht Pythonic, aber es macht Sinn.

14
quick_dry

Das ist nicht der schönste Weg, aber meiner Meinung nach der beste Weg. 

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

Ich bin mir ziemlich sicher, dass Sie auch hier etwas mit Rekursion ausarbeiten könnten, aber ich weiß nicht, ob das eine gute Option für Sie ist.

11
Jason Baker

Um aus mehreren verschachtelten Schleifen auszubrechen, ohne in eine Funktion umgewandelt zu werden, verwenden Sie eine "simulierte goto-Anweisung" mit der integrierten StopIteration-Ausnahme :

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

except StopIteration: pass

Siehe diese Diskussion über die Verwendung von goto-Anweisungen zum Ausbrechen von verschachtelten Schleifen.

10
Justas

Berechnen Sie Ihre Schleifenlogik in einen Iterator, der die Schleifenvariablen liefert und zurückgibt, wenn Sie fertig sind. Hier ist eine einfache, die Bilder in Zeilen/Spalten anordnet, bis wir keine Bilder mehr haben oder außerhalb von Orten, um sie zu platzieren:

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 ...

Dies hat den Vorteil, dass die komplizierte Schleifenlogik und die Verarbeitung ...

8

Und warum sollte man nicht weiterlaufen, wenn zwei Bedingungen zutreffen?

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

Ist es nicht

Alles Gute.

7
Mauro Aspé

In diesem Fall, wie auch von anderen aufgezeigt, ist funktionelle Zerlegung der Weg. Code in 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
Loax

In der Python while ... else-Struktur gibt es einen versteckten Trick, mit dem der doppelte Umbruch ohne viel Codeänderungen ergänzungen simuliert werden kann. Wenn im Wesentlichen die while-Bedingung falsch ist, wird der else-Block ausgelöst. Keine der Ausnahmen, continue oder break, löst den else-Block aus. Weitere Informationen finden Sie in den Antworten auf "/ - Else-Klausel auf Python while-Anweisung " oder auf Python doc on 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

Der einzige Nachteil ist, dass Sie die doppelte Unterbrechungsbedingung in die while-Bedingung verschieben müssen (oder eine Flag-Variable hinzufügen). Variationen davon gibt es auch für die for-Schleife, bei der der else-Block nach Abschluss der Schleife ausgelöst wird.

3
holroy

Eine andere Möglichkeit, die Iteration auf eine einstufige Schleife zu reduzieren, besteht in der Verwendung von Generatoren, die ebenfalls in der Referenz python angegeben sind.

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

Sie können es für die Schleife auf eine beliebige Anzahl von Stufen skalieren

Der Nachteil ist, dass Sie nicht mehr nur eine Ebene brechen können. Alles oder Nichts.

Ein weiterer Nachteil ist, dass es nicht mit einer while-Schleife funktioniert. Ich wollte diese Antwort ursprünglich auf Python - "break" aus allen Schleifen posten, aber leider ist dies als Duplikat dieser hier geschlossen 

2

Mein Grund, hierher zu kommen, ist, dass ich eine äußere und eine innere Schleife hatte:

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

Wie Sie sehen, geht es nicht wirklich zum nächsten x, sondern zum nächsten y.

um dies zu lösen, musste ich einfach zweimal durch das Array laufen:

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

Ich weiß, dass dies ein spezieller Fall von OPs Frage war, aber ich poste es in der Hoffnung, dass es jemandem helfen wird, sein Problem anders zu denken und dabei die Dinge einfach zu halten.

2

Mit einer Funktion:

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

Führen Sie die obigen Codes aus, indem Sie auch die Variable return auskommentieren.

Ohne Verwendung einer Funktion:

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

Führen Sie nun die obigen Codes aus, und starten Sie dann die Ausführung, indem Sie jede Zeile, die break enthält, einzeln von unten auskommentieren.

2
Rafiq
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
1
RufusVS

wahrscheinlich wird ein kleiner Trick wie unten, wenn nicht lieber in die Funktion umgestaltet

1 Variable break_level hinzugefügt, um die while-Schleifenbedingung zu steuern

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
Skycc
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
RufusVS

Versuchen Sie es mit einem unendlichen Generator.

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
1
robert king

Sie können eine Variable definieren (z. B. break_statement ) und sie dann in einen anderen Wert ändern, wenn eine Zwei-Break-Bedingung auftritt, und sie in der if-Anweisung verwenden, um auch die zweite Schleife zu unterbrechen. 

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
1
helmsdeep

Eine einfache Möglichkeit, mehrere Schleifen in eine einzelne, zerbrechliche Schleife zu verwandeln, ist 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!

Sie müssen in Ihre Objekte indizieren, anstatt die Werte explizit durchlaufen zu können, aber in einfachen Fällen scheint es ungefähr 2-20-mal einfacher als die meisten vorgeschlagenen Antworten zu sein.

0
one_observation

Ich möchte Sie daran erinnern, dass Funktionen in Python direkt in der Mitte des Codes erstellt werden können und auf die Umgebungsvariablen transparent zum Lesen und mit der Erklärung nonlocal oder global zum Schreiben zugreifen können.

Sie können also eine Funktion als "zerbrechliche Kontrollstruktur" verwenden, die einen Ort definiert, zu dem Sie zurückkehren möchten:

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
0
user

Lösungen auf zwei Wegen

Mit einem Beispiel: Sind diese beiden Matrizen gleich?
Matrix1 und Matrix2 haben dieselbe Größe, n, 2 dimensionale Matrizen.

Erste Lösung , ohne Funktion

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

Zweite Lösung , mit einer Funktion
Dies ist die endgültige Lösung für meinen Fall

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

Einen schönen Tag noch!

0
Harun Altay

Hoffentlich hilft das:

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
Daniel L.