Python: Fortsetzung der nächsten Iteration in der äußeren Schleife

135

Ich wollte wissen, ob es integrierte Möglichkeiten gibt, um mit der nächsten Iteration in der äußeren Schleife in Python fortzufahren. Betrachten Sie zum Beispiel den Code:

for ii in range(200):
    for jj in range(200, 400):
        ...block0...
        if something:
            continue
    ...block1...

Ich möchte, dass diese continue-Anweisung die jj-Schleife verlässt und zum nächsten Element in der ii-Schleife wechselt. Ich kann diese Logik auf eine andere Weise implementieren (indem ich eine Flag-Variable setze), aber gibt es eine einfache Möglichkeit, dies zu tun, oder ist dies so, als würde man zu viel verlangen?

Sahas
quelle
11
Es gibt tatsächlich eine funktionierende goto-Anweisung für Python: entrian.com/goto . Es wurde als Aprilscherz veröffentlicht :-), soll aber funktionieren.
Codeape
3
Oh, bitte benutze diesen Scherz nicht! Es ist bemerkenswert klug, aber Sie werden später traurig sein, wenn Sie es in Ihren Code einfügen.
Ned Batchelder

Antworten:

71
for i in ...:
    for j in ...:
        for k in ...:
            if something:
                # continue loop i

Im Allgemeinen können Sie eine der folgenden Aktionen ausführen, wenn Sie mehrere Schleifenebenen haben und breakfür Sie nicht funktionieren (weil Sie eine der oberen Schleifen fortsetzen möchten, nicht die direkt über der aktuellen)

Refaktorieren Sie die Schleifen, aus denen Sie entkommen möchten, in eine Funktion

def inner():
    for j in ...:
        for k in ...:
            if something:
                return


for i in ...:
    inner()

Der Nachteil ist, dass Sie möglicherweise einige Variablen an diese neue Funktion übergeben müssen, die zuvor im Gültigkeitsbereich waren. Sie können sie entweder einfach als Parameter übergeben, als Instanzvariablen für ein Objekt festlegen (ein neues Objekt nur für diese Funktion erstellen, wenn dies sinnvoll ist) oder als globale Variablen, Singletons (ehm, ehm).

Oder Sie können innereine verschachtelte Funktion definieren und sie nur erfassen lassen, was sie benötigt (möglicherweise langsamer?)

for i in ...:
    def inner():
        for j in ...:
            for k in ...:
                if something:
                    return
    inner()

Verwenden Sie Ausnahmen

Philosophisch gesehen sind dies Ausnahmen, die den Programmfluss durch die strukturierten Programmierbausteine ​​(wenn, für, während) bei Bedarf unterbrechen.

Der Vorteil ist, dass Sie den einzelnen Code nicht in mehrere Teile aufteilen müssen. Dies ist gut, wenn es sich um eine Art Berechnung handelt, die Sie beim Schreiben in Python entwerfen. Das Einführen von Abstraktionen an diesem frühen Punkt kann Sie verlangsamen.

Das Schlechte an diesem Ansatz ist, dass Interpreter / Compiler-Autoren normalerweise davon ausgehen, dass Ausnahmen außergewöhnlich sind, und sie entsprechend optimieren.

class ContinueI(Exception):
    pass


continue_i = ContinueI()

for i in ...:
    try:
        for j in ...:
            for k in ...:
                if something:
                    raise continue_i
    except ContinueI:
        continue

Erstellen Sie hierfür eine spezielle Ausnahmeklasse, damit Sie nicht versehentlich eine andere Ausnahme stumm schalten.

Etwas ganz anderes

Ich bin sicher, dass es noch andere Lösungen gibt.

user7610
quelle
Ich kann nicht glauben, dass ich nicht daran gedacht habe, meine zweite Schleife auf eine andere Methode zu verschieben. Ich werde langsam
pmccallum
1
Die Verwendung von Ausnahmen ist für mich ein guter Weg, um dies zu erreichen. Ich stimme @ user7610 zu - "Philosophisch gesehen sind dies Ausnahmen".
Renato Byrro
Gute alte boolesche Variable und If-Anweisungen?
MrR
Das OP sucht nach alternativen Lösungen dafür: "Ich kann diese Logik auf andere Weise implementieren (indem ich eine Flag-Variable
setze
149
for ii in range(200):
    for jj in range(200, 400):
        ...block0...
        if something:
            break
    else:
        ...block1...

Break wird die innere Schleife unterbrechen und Block1 wird nicht ausgeführt (es wird nur ausgeführt, wenn die innere Schleife normal verlassen wird).

culebrón
quelle
1
Hallo, gibt es noch andere Möglichkeiten wie diese? Weil ich eine weitere for-Schleife in Block1 machen möchte und mein Code so 3 Ebenen tief gehen würde. Seltsame Situation.
Sahas
3
Für mich klingt das so, als würden Sie versuchen, etwas für Schleifen zu tun, die am besten auf andere Weise
angegangen werden könnten
Ja. Deshalb habe ich die for..else-Struktur nicht verwendet. Jetzt brauche ich noch Schleifen, aber ich werde Flag-Variablen verwenden, um die Kontrolle umzuleiten.
Sahas
3
for...elseist oft ein nützliches Konstrukt, obwohl es verwirrend sein kann. Denken Sie daran, dass elsedies in diesem Zusammenhang "keine Pause" bedeutet.
Asmeurer
Dies scheint auf nur zwei Schichten von Schleifen beschränkt zu sein. Ich muss einen Code aktualisieren, der drei verschachtelte Schleifen enthält, und eine neue Kundenanforderung bedeutet, dass die innerste Schleife unter bestimmten Umständen die nächste Iteration der äußersten Schleife fortsetzen muss. Ich gehe davon aus, dass Ihr Vorschlag für dieses Szenario nicht gilt.
Kasperd
42

In anderen Sprachen können Sie die Schleife beschriften und von der beschrifteten Schleife abbrechen. Python Enhancement Proposal (PEP) 3136 schlug vor, diese zu Python hinzuzufügen, aber Guido lehnte dies ab :

Ich lehne es jedoch mit der Begründung ab, dass Code, der so kompliziert ist, dass diese Funktion erforderlich ist, sehr selten ist. In den meisten Fällen gibt es Workarounds, die sauberen Code erzeugen, z. B. mit 'return'. Ich bin mir sicher, dass es einige (seltene) reale Fälle gibt, in denen die Klarheit des Codes durch ein Refactoring beeinträchtigt wird, das die Verwendung von return ermöglicht. Dies wird jedoch durch zwei Probleme ausgeglichen:

  1. Die Komplexität, die der Sprache dauerhaft hinzugefügt wurde. Dies betrifft nicht nur alle Python-Implementierungen, sondern auch jedes Quellanalyse-Tool sowie natürlich die gesamte Dokumentation für die Sprache.

  2. Meine Erwartung, dass die Funktion mehr missbraucht wird als richtig verwendet wird, führt zu einer Nettoverringerung der Codeklarheit (gemessen über den gesamten von nun an geschriebenen Python-Code). Faule Programmierer sind überall, und bevor Sie es wissen, haben Sie ein unglaubliches Durcheinander von unverständlichem Code in Ihren Händen.

Wenn Sie also gehofft haben, dass Sie kein Glück haben, schauen Sie sich eine der anderen Antworten an, da es dort gute Möglichkeiten gibt.

Dave Webb
quelle
4
Interessant. Ich stimme Guido hier zu. Während es für einige Fälle schön wäre, würde es missbraucht werden. Ein weiterer Grund für die Nichtimplementierung ist, dass es im Moment ziemlich einfach ist, Code zwischen C und Python hin und her zu portieren. Sobald Python Funktionen aufnimmt, die anderen Sprachen fehlen, wird dies schwieriger. Nehmen wir zum Beispiel die Tatsache, dass Sie eine else-Anweisung in einer for-Schleife in Python haben können ... dies macht Code weniger portabel für andere Sprachen.
Eric.Frederich
2
Alle
begrüßen
4
Dies ist eher ein Red-Heiring als ein gutes Gegenargument, aber es scheint mir, dass das Verhalten von for-elsekomplizierter, schwieriger zu lesen und wahrscheinlich missbrauchter (wenn nicht ein völliger Fehler) ist als benannte Schleifen. Ich glaube, ich hätte ein anderes Schlüsselwort verwendet als else- vielleicht resumewäre so etwas gut gewesen? Sie breakin der Schleife und das resumeist gleich danach?
ArtOfWarfare
5
Das macht mich traurig. Ich kann nicht glauben, wie ich Python gleichzeitig liebe und hasse. So schön und doch so wtf.
jlh
5
@jlh Meistens wtf für mich. Manchmal denke ich, dass es anders sein will, nicht aus legitimen Gründen, sondern nur um anders zu sein. Dies ist ein gutes Beispiel dafür. Ich stoße ziemlich oft auf die Notwendigkeit, äußere Schleifen zu durchbrechen.
Rikaelus
14

Ich denke, Sie könnten so etwas tun:

for ii in range(200):
    restart = False
    for jj in range(200, 400):
        ...block0...
        if something:
            restart = True
            break
    if restart:
        continue
    ...block1...
asmeurer
quelle
4
-1: Das OP gab eindeutig an, dass sie wussten, dass sie so etwas tun können, und dies sieht nur nach einer chaotischeren Version der akzeptierten Antwort aus (die 8 Monate vor Ihrer liegt, also konnte es nicht sein, dass Sie die akzeptierte Antwort einfach verpasst haben Antworten).
ArtOfWarfare
10
Die akzeptierte Antwort ist nicht klarer, wenn Sie sie noch nie gesehen forhaben else(und ich denke, selbst die meisten Menschen, die sich nicht erinnern können, wie es funktioniert).
Asmeurer
3

Ich denke, eine der einfachsten Möglichkeiten, dies zu erreichen, besteht darin, "continue" durch die Anweisung "break" zu ersetzen, d. H.

for ii in range(200):
 for jj in range(200, 400):
    ...block0...
    if something:
        break
 ...block1...       

Hier ist zum Beispiel der einfache Code, um zu sehen, wie genau es weitergeht:

for i in range(10):
    print("doing outer loop")
    print("i=",i)
    for p in range(10):
        print("doing inner loop")
        print("p=",p)
        if p==3:
            print("breaking from inner loop")
            break
    print("doing some code in outer loop")
Khelina Fedorchuk
quelle
2

Eine andere Möglichkeit, mit dieser Art von Problem umzugehen, ist die Verwendung von Exception ().

for ii in range(200):
    try:
        for jj in range(200, 400):
            ...block0...
            if something:
                raise Exception()
    except Exception:
        continue
    ...block1...

Beispielsweise:

for n in range(1,4):
    for m in range(1,4):
        print n,'-',m

Ergebnis:

    1-1
    1-2
    1-3
    2-1
    2-2
    2-3
    3-1
    3-2
    3-3

Angenommen, wir möchten von der m-Schleife zur äußeren n-Schleife springen, wenn m = 3:

for n in range(1,4):
    try:
        for m in range(1,4):
            if m == 3:
                raise Exception()            
            print n,'-',m
    except Exception:
        continue

Ergebnis:

    1-1
    1-2
    2-1
    2-2
    3-1
    3-2

Referenzlink: http://www.programming-idioms.org/idiom/42/continue-outer-loop/1264/python

Patrick
quelle
1

Wir wollen etwas finden und dann die innere Iteration stoppen. Ich benutze ein Flaggensystem.

for l in f:
    flag = True
    for e in r:
        if flag==False:continue
        if somecondition:
            do_something()
            flag=False
Esther
quelle
Ich weiß nicht, warum Ihre Lösung abgelehnt wurde. Jemand hat im Grunde genau das Gleiche gepostet und wurde 10 Mal hochgestuft
Locane
Ich bin nicht sehr glücklich mit Stackoverflow.
Esther
1
Vielleicht, weil es hier im Grunde schon genau dasselbe gibt, denke ich ... Und die False:continueSache ist ... ungewöhnliche Formatierung. Wie es häufig in "natürlichen" Systemen der Fall ist, in denen Exponential die Norm sind, müssen Sie bei SO nur ein paar Mal Glück haben, um eine signifikante Anzahl von Reputationspunkten zu sammeln. Wie auch immer, meine "besten" Antworten sind normalerweise die am wenigsten populären.
user7610
0

Ich habe so etwas einfach gemacht. Meine Lösung bestand darin, das Innere der for-Schleife durch ein Listenverständnis zu ersetzen.

for ii in range(200):
    done = any([op(ii, jj) for jj in range(200, 400)])
    ...block0...
    if done:
        continue
    ...block1...

Dabei ist op ein boolescher Operator, der auf eine Kombination von ii und jj einwirkt. In meinem Fall war ich fertig, wenn eine der Operationen true zurückgab.

Dies unterscheidet sich wirklich nicht wesentlich von der Aufteilung des Codes in eine Funktion, aber ich fand es interessant, den Operator "any" zu verwenden, um ein logisches ODER für eine Liste von Booleschen Werten zu erstellen und die Logik in einer Zeile auszuführen. Es vermeidet auch den Funktionsaufruf.

cagem12
quelle