713

Given the following code (that doesn't work):

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

Is there a way to make this work? Or do I have do one check to break out of the input loop, then another, more limited, check in the outside loop to break out all together if the user is satisfied?

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
Matthew Scharley
  • 127,823
  • 52
  • 194
  • 222
  • 193
    Why doesn't Python just have 'break(n)' where n is the number of levels you want to break out of. – Nathan May 02 '10 at 16:49
  • 14
    C++ is nice here with `goto` if you're nestled deep in a lot of loops – Drake Johnson Dec 12 '19 at 14:50
  • 1
    @Nathan See [Why python don't natively allow a keyword like goto for breaking out of n loops](https://stackoverflow.com/a/653517/11667949) a very nice explanation by nathan – Shivam Jha Oct 09 '20 at 07:34
  • 2
    Ned Batchelder has this interesting talk that addresses ["How do I break out of two loops?"](https://youtu.be/EnSu9hHGq5o?t=1184). Spoiler alert: Make the double loop single. – Tiago Martins Peres Dec 03 '20 at 09:28

39 Answers39

722

My first instinct would be to refactor the nested loop into a function and use return to break out.

Robert Rossney
  • 94,622
  • 24
  • 146
  • 218
  • 170
    agreed in this specific case, but in the general case of 'I have nested loops, what do I do' refactoring may not make sense. – quick_dry Oct 10 '08 at 01:11
  • using an exception may be easier when you must yield instead of using return, however you probably should be using itertools.islice() in such a case. – Rusty Rob Feb 13 '12 at 03:53
  • How could `islice` be relevant here? And why would you be forced to `yield` in an inner function that doesn't yield (or build and return) anything? And even if you have a different case in mind, where the loop _is_ yielding, you can still factor it into a function; just use `yield from` (3.3+), a `for` loop around `yield` (3.2-), or `return` the generator iterator instead of `yield`ing it (either), as appropriate. – abarnert Mar 21 '13 at 23:08
  • 6
    Its usually possible to refactor the inner loop into its own method, that returns true to continue, false to break the outer loop. while condition1: / if not MyLoop2(params): break. An alternative is to set a boolean flag, that is tested at both levels. more = True / while condition1 and more: / while condition2 and more: / if stopCondition: more = False / break / ... – ToolmakerSteve Nov 22 '13 at 19:44
  • 8
    I agree that striving to use `return` is the right approach. And the reasoning is that, according to the _Zen of Python_, "flat is better than nested". We have three levels of nesting here and if that starts to get in the way, it is time to reduce the nesting or at least extract the whole nesting into a function of its own. – Lutz Prechelt Apr 03 '14 at 10:14
  • Using `return` is a good idea in general, but I found that list comprehensions can help too. – VladFr May 24 '15 at 10:33
  • 2
    Nested loops are just necessary. You shouldn't be nesting more than say 3 ever, but nested loops are what make seeks and scans work, so even if you aren't coding them yourself you're using them. There must be a way to break multiple loops as that is a basic necessity of all other programming languages that I know of. – Jamie Marshall Jul 22 '18 at 00:22
  • 1
    @JamieMarshall Except Python does fine without it. If you have a code so complex that you need a "labelled break" then you are probably doing something wrong anyway. In any case you can always use `raise` to simulate labelled `break`. Create a new `Exception` instance for every different `label`, use `raise LabelException()` instead of `break label` and wrap in `try: ... except LabelException: ` the code that needs to jump to that label. If your code is that complex to need labels adding some exceptions classes a `raise` and an `except` wont be that bad, the code is ugly anyway. – Giacomo Alzetta Nov 05 '19 at 17:28
  • but the thing is what would you return, I mean you can't just return a random thing? – ThatOneAmazingPanda Nov 26 '20 at 19:10
  • 5
    I know it may seem obvious, but an example using the original code would improve this answer. – otocan Mar 26 '21 at 15:37
  • this is a good suggestion but I'd also like the option of `BreakAllLoops` option...for now I have a boolean flag that if halt is true then everyone halts but there are lots of if statements...in fact in my case I explicitly don't want to write a separate function. – Charlie Parker Sep 29 '21 at 20:57
  • y is this accepted lol – dQw4w9WyXcQ Aug 14 '23 at 09:04
476

Here's another approach that is short. The disadvantage is that you can only break the outer loop, but sometimes it's exactly what you want.

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

This uses the for / else construct explained at: Why does python use 'else' after for and while loops?

Key insight: It only seems as if the outer loop always breaks. But if the inner loop doesn't break, the outer loop won't either.

The continue statement is the magic here. It's in the for-else clause. By definition that happens if there's no inner break. In that situation continue neatly circumvents the outer break.

Ciro Santilli OurBigBook.com
  • 347,512
  • 102
  • 1,199
  • 985
yak
  • 4,817
  • 1
  • 15
  • 2
  • @RishitBansal Although this is a deep cut: The outer loop does matter because the inner break condition `something(a, b)` depends on `a` too. The outer loop may run as long as `something(a, b)` is not `True`. – clemisch May 18 '18 at 08:05
  • 3
    Got this from a Raymond Hettinger video, https://youtu.be/OSGv2VnC0go?t=971, read "else" statements attached to for loops as "no_break", then it becomes easier to understand. – Ambareesh Oct 29 '19 at 04:45
  • 17
    This is clever. :-) However, not straight-forward. Frankly, I am not convinced by arguments to keep labeled break or break(n) out of Python. The workarounds add more complexity. – rfportilla Apr 01 '20 at 21:11
  • 2
    This will not work in the following situation. If there are two breaks in the inner loop with one intended to break only inner loop while the other intended to break both loops – Abdelmonem Mahmoud Amer Jan 09 '21 at 11:30
  • cool but super hard to read. – WestCoastProjects Nov 08 '21 at 22:14
  • the else continue block here is useless as it will still continue if you put it or not tho gave it a upvote since i did not know for loops had a else block – ahmed mani Jan 11 '22 at 02:10
  • If I see for/else combined with 3 `break`/`continue` statements, you'd bet I'm throwing this code out. It's extremely hard to read and also confusing for everyone. – Alex Mandelias Aug 04 '23 at 15:35
  • Just what I was looking for! – TheDronist Aug 24 '23 at 10:08
181

PEP 3136 proposes labeled break/continue. Guido rejected it because "code so complicated to require this feature is very rare". The PEP does mention some workarounds, though (such as the exception technique), while Guido feels refactoring to use return will be simpler in most cases.

John Fouhy
  • 41,203
  • 19
  • 62
  • 77
  • 102
    Although, refactor/`return` is usually the way to go, I've seen quite a few cases where a simple concise ‘`break 2`’ statement would just make so much sense. Also, refactor/`return` doesn't work the same for `continue`. In these cases, numeric break and continue would be easier to follow and less cluttered than refactoring to a tiny function, raising exceptions, or convoluted logic involving setting a flag to break at each nest level. It's a shame Guido rejected it. – James Haigh Jul 11 '13 at 18:29
  • 14
    `break; break` would be nice. – PyRulez Jul 17 '13 at 21:13
  • 10
    @Jeyekomon The problem is that you don't need 3 or more nested loops for this to be a problem. 2 nested loops are pretty common – Jon Apr 06 '18 at 21:20
  • @Jon I know. Are two loops big enough problem to create a syntax in python for that, especially when there are several existing workarounds? A tough question. And at the end someone will end up unhappy, however you decide. Python is aimed to be a powerful language, yet still with unbloated syntax. – Jeyekomon Apr 07 '18 at 18:06
  • 14
    "Code so complicated to require this feature is very rare". But if you ever do use code this complicated, the lack of labeled loops will make it even more complicated, as you must manually forward the `break` through all of the loops. Stupid. – BallpointBen Apr 13 '18 at 22:57
  • @James Haigh I don't think its a shame. Python is already cluttered enough without it. I hope it never becomes as cluttered as C++. Everything that you could use a multi-level break for can be achieved by using `for; else` or `if; else` constructs. – Bachsau Apr 18 '18 at 18:54
  • @PyRulez `break; break` is already valid syntax; it's just two consecutive break statements, same as if they were written on consecutive lines. It therefore makes little sense as a candidate syntax for doing this. Rather than introducing some weird syntactic special case where code that would otherwise parse as two statements gets an alternate meaning, it'd be cleaner and less confusing to support numbered break (e.g. `break 2`) as other languages do and the PEP suggests. – Mark Amery Apr 22 '19 at 10:31
  • 3
    Apparently, I can only edit a post for 5 minutes (it's been 6). So, here's my edited post: My 2 cents: Perl has labeled break (but calls it 'last') and 'next' to proceed directly to the next iteration. It is not at all rare - I use it all the time. I'm brand new to Python and already have a need for it. Also, numbered breaks would be horrible for refactoring - better to label the loop that you want to break out of, then use break – John Deighan May 26 '19 at 19:00
158

First, ordinary logic is helpful.

If, for some reason, the terminating conditions can't be worked out, exceptions are a fall-back plan.

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

For this specific example, an exception may not be necessary.

On other other hand, we often have "Y", "N" and "Q" options in character-mode applications. For the "Q" option, we want an immediate exit. That's more exceptional.

Russia Must Remove Putin
  • 374,368
  • 89
  • 403
  • 331
S.Lott
  • 384,516
  • 81
  • 508
  • 779
  • 11
    Seriously, exceptions are *extremely* cheap and idiomatic python uses lots and lots of them. It's very easy to define and throw custom ones, as well. – Gregg Lind Oct 21 '08 at 20:56
  • 11
    This solution would be more helpful, if it showed the two variations separately. (1) using a flag (`done`). (2) raising an exception. Merging them together into a single solution just makes it look complicated. For future readers: EITHER use all the lines involving `done`, OR define `GetOutOfLoop(Exception)` and raise/except that. – ToolmakerSteve Dec 18 '13 at 00:25
  • 4
    In general, using try-blocks for anything other then exceptions is very frowned upon. Try-blocks are specifically designed for error handling, and using them for some strange control flow is not very good, stylistically. – nobillygreen Dec 26 '13 at 17:28
  • I wonder what the BDFL thinks about this kind of use of exceptions... not that his word is Law, of course, but I'd be interested to know. – mike rodent Feb 14 '16 at 12:36
  • 2
    In Python3 this would be `raise Exception('GetOutOfLoop')` and `except Exception:`. – tommy.carstensen Jan 08 '17 at 14:37
  • 4
    @tommy.carstensen That's nonsense; both defining a new exception subclass and raising it (as shown in the answer) and passing a custom message to the `Exception` constructor (e.g. `raise Exception('bla bla bla')`) are valid in both Python 2 and Python 3. The former is preferable in this case because we don't want our `except` block to catch *all* exceptions, but only the special exception we're using to exit the loop. If we do things the way you suggest, and then a bug in our code causes an unexpected exception to be raised, it'll be wrongly treated the same as deliberately exiting the loop. – Mark Amery Apr 22 '19 at 11:08
  • @GreggLind: even if they are cheap in Python (they are expensive in many other languages), do you want the debugger to break every second when an exception is thrown? – Thomas Weller Jun 24 '19 at 07:19
  • Exception should be used only for "exception" this will make debugging of "real" exceptions painful unless u use very good ide – Petr Apr 22 '21 at 19:05
  • So break 2 was rejected because of too much indentation, but the workaround uses one more level of it... – lalala Nov 27 '21 at 18:00
70

Introduce a new variable that you'll use as a 'loop breaker'. First assign something to it(False,0, etc.), and then, inside the outer loop, before you break from it, change the value to something else(True,1,...). Once the loop exits make the 'parent' loop check for that value. Let me demonstrate:

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

If you have an infinite loop, this is the only way out; for other loops execution is really a lot faster. This also works if you have many nested loops. You can exit all, or just a few. Endless possibilities! Hope this helped!

krvolok
  • 811
  • 6
  • 4
65

I tend to agree that refactoring into a function is usually the best approach for this sort of situation, but for when you really need to break out of nested loops, here's an interesting variant of the exception-raising approach that @S.Lott described. It uses Python's with statement to make the exception raising look a bit nicer. Define a new context manager (you only have to do this once) with:

from contextlib import contextmanager

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

Now you can use this context manager as follows:

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

Advantages: (1) it's slightly cleaner (no explicit try-except block), and (2) you get a custom-built Exception subclass for each use of nested_break; no need to declare your own Exception subclass each time.

Mark Dickinson
  • 29,088
  • 9
  • 83
  • 120
47

First, you may also consider making the process of getting and validating the input a function; within that function, you can just return the value if its correct, and keep spinning in the while loop if not. This essentially obviates the problem you solved, and can usually be applied in the more general case (breaking out of multiple loops). If you absolutely must keep this structure in your code, and really don't want to deal with bookkeeping booleans...

You may also use goto in the following way (using an April Fools module from here):

#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

I know, I know, "thou shalt not use goto" and all that, but it works well in strange cases like this.

Matt J
  • 43,589
  • 7
  • 49
  • 57
  • I'm at college at the moment, quickly (since goto's not in the module index), what is comefrom for? – Matthew Scharley Oct 10 '08 at 00:20
  • 1
    If it is anything like the COME FROM command in INTERCAL, then nothing – 1800 INFORMATION Oct 10 '08 at 00:28
  • comefrom in Python allows you to redirect the running program to a different place whenever it reaches a certain label. There's more info here (http://entrian.com/goto/) – Matt J Oct 10 '08 at 00:30
  • In other words, it's a really bad idea unless you love your spaghetti code. – Matthew Scharley Oct 10 '08 at 00:36
  • As mentioned on the page I linked to, it's a quick and dirty way to alter your code flow during debug/bringup, but it's definitely not a production sort of thing. – Matt J Oct 10 '08 at 00:45
  • @Matt J: I like your use of a April Fool's farce (even if it works!), but let's be more serious. Do you really, honestly suggest using the goto module? – tzot Oct 10 '08 at 01:55
  • I don't purport this to be a pythonic or "clean" solution, but it *is* a paradigm that is used in other languages *when necessary*. I also suggested encapsulating the inner loop in a function, which I consider to be "better" but self-explanatory – Matt J Oct 10 '08 at 02:05
  • using goto is danger because most of you doesn't understand how the stack works, this is compresible for those which have experiencie with any kind of assembler language, in my case, PIC microchips. Every nested loop in general takes one slot of memory, and break and returns release that slot. Goto doesn't release the memory, so in time you can overload the stack memory. Though, I'm really not sure if modern compilers are cared about this – Daniel N. Mar 19 '15 at 01:38
  • 1
    @J.T.Hurley no this is not clean and readable. I mean, it may look like it is clean and readable *in this example* but in any real life scenario goto's create a *holy mess*. (Also this is sooo anti-pythonic...) – Alois Mahdal Aug 18 '15 at 00:46
  • "Strange cases like this" - really, that case is not strange and very well solvable without a crappy goto statement – Thomas Weller Jun 24 '19 at 07:23
45

To break out of multiple nested loops, without refactoring into a function, make use of a "simulated goto statement" with the built-in StopIteration exception:

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

except StopIteration: pass

See this discussion on the use of goto statements for breaking out of nested loops.

Justas
  • 5,718
  • 2
  • 34
  • 36
  • 2
    In fact StopIteration is using for generators, but I think normally you don't have any uncatched StopIteration exception. So it seems like a good solution but there is not a mistake at creating new exception anyway. – Ekrem Dinçel Jan 23 '20 at 18:52
23
keeplooping = True
while keeplooping:
    # Do stuff
    while keeplooping:
          # Do some other stuff
          if finisheddoingstuff():
              keeplooping = False

or something like that.

You could set a variable in the inner loop, and check it in the outer loop immediately after the inner loop exits, breaking if appropriate. I kind of like the GOTO method, provided you don't mind using an April Fool's joke module - it’s not Pythonic, but it does make sense.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
quick_dry
  • 811
  • 5
  • 11
13

This isn't the prettiest way to do it, but in my opinion, it's the best way.

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

I'm pretty sure you could work out something using recursion here as well, but I don't know if that's a good option for you.

peterh
  • 11,875
  • 18
  • 85
  • 108
Jason Baker
  • 192,085
  • 135
  • 376
  • 510
12

Keep looping if two conditions are true.

I think this is a more Pythonic way:

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
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Mauro
  • 817
  • 1
  • 9
  • 11
  • why not just `while dejaVu:`? You set it to true anyway. – Matthew Scharley Nov 06 '12 at 23:23
  • 2
    @MatthewScharley I think this is to show that this works in nested loops. – handle Nov 20 '14 at 16:07
  • @MauroAspé this will not exactly do what the OP requests. it will still execute the whole outer loop but the target is that if you break the rest of the code will not get executed – yamm May 27 '15 at 07:55
11

There is no way to do this from a language level. Some languages have a goto others have a break that takes an argument, python does not.

The best options are:

  1. Set a flag which is checked by the outer loop, or set the outer loops condition.

  2. Put the loop in a function and use return to break out of all the loops at once.

  3. Reformulate your logic.

Credit


Using Function

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

Using flag

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
0xInfection
  • 2,676
  • 1
  • 19
  • 34
8

Factor your loop logic into an iterator that yields the loop variables and returns when done -- here is a simple one that lays out images in rows/columns until we're out of images or out of places to put them:

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

This has the advantage of splitting up the complicated loop logic and the processing...

5

An easy way to turn multiple loops into a single, breakable loop is to use 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!

You do have to index into your objects, as opposed to being able to iterate through the values explicitly, but at least in simple cases it seems to be approximately 2-20 times simpler than most of the answers suggested.

one_observation
  • 454
  • 5
  • 16
4

In this case, as pointed out by others as well, functional decomposition is the way to go. 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
Loax
  • 615
  • 5
  • 11
4

There is a hidden trick in the Python while ... else structure which can be used to simulate the double break without much code changes/additions. In essence if the while condition is false, the else block is triggered. Neither exceptions, continue or break trigger the else block. For more information see answers to "Else clause on Python while statement", or 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

The only downside is that you need to move the double breaking condition into the while condition (or add a flag variable). Variations of this exists also for the for loop, where the else block is triggered after loop completion.

Community
  • 1
  • 1
holroy
  • 3,047
  • 25
  • 41
  • This does not seem to fulfill the requirement of double breaks. Works for the exact given problem, but not for the actual question. – Dakkaron Oct 05 '16 at 13:39
  • 1
    @Dakkaron Are you sure you've understood the code correctly? The code does indeed solve the OPs question, and breaks similar to request. It does however not break out of multiple loops, but use the else clause to replace the need of doubling the break. – holroy Oct 05 '16 at 13:46
  • From my understanding the question was `How to break out of multiple loops in Python?` and the answer should have been "It does not work, try something else". I know it fixes the exact given example of the OP, but does not answer their question. – Dakkaron Oct 05 '16 at 14:02
  • 1
    @Dakkaron, See the problem statement under the code, and in my opinion it does indeed answer the OPs question. – holroy Oct 05 '16 at 14:13
4

By using a function:

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

Try running the above codes by commenting out the return as well.

Without using any function:

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

Now, run the above codes as is first and then try running by commenting out each line containing break one at a time from the bottom.

Rafiq
  • 1,380
  • 4
  • 16
  • 31
3

Another way of reducing your iteration to a single-level loop would be via the use of generators as also specified in the python reference

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

You could scale it up to any number of levels for the loop

The downside is that you can no longer break only a single level. It's all or nothing.

Another downside is that it doesn't work with a while loop. I originally wanted to post this answer on Python - `break` out of all loops but unfortunately that's closed as a duplicate of this one

Community
  • 1
  • 1
Peeyush Kushwaha
  • 3,453
  • 8
  • 35
  • 69
  • 1
    It works for while loops too, you only need to write your generator as a def (with yield), not as a comprehension. – Veky Apr 10 '17 at 10:23
  • Yes, [a speaker at a PyCon claims here](https://youtu.be/EnSu9hHGq5o?t=19m38s) that even @RobertRossney's accepted answer is not truly Pythonic, but a generator is the right way to break multiple loops. (I'd recommend watching the whole video!) – Post169 Mar 19 '18 at 21:55
3

I'd like to remind you that functions in Python can be created right in the middle of the code and can access the surrounding variables transparently for reading and with nonlocal or global declaration for writing.

So you can use a function as a "breakable control structure", defining a place you want to return to:

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
user
  • 23,260
  • 9
  • 113
  • 101
2

Try using an infinite 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
MichaelChirico
  • 33,841
  • 14
  • 113
  • 198
Rusty Rob
  • 16,489
  • 8
  • 100
  • 116
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
RufusVS
  • 4,008
  • 3
  • 29
  • 40
2
# 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
RufusVS
  • 4,008
  • 3
  • 29
  • 40
2

Solutions in two ways

With an example: Are these two matrices equal/same?
matrix1 and matrix2 are the same size, n, two-dimensional matrices.

First solution, without a function

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

Second solution, with a function

This is the final solution for my case.

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
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
2

Here's an implementation that seems to work:

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)

The only draw back is that you have to define break_ before the loops.

Fateh
  • 302
  • 2
  • 12
2

What I would personally do is use a boolean that toggles when I am ready to break out the outer loop. For example

while True:
    #snip: print out current state
    quit = False
    while True:
        ok = input("Is this ok? (y/n)")
        if ok.lower() == "y":
            quit = True
            break # this should work now :-)
        if ok.lower() == "n":
            quit = True
            break # This should work too :-)
    if quit:
        break
    #do more processing with menus and stuff
kcEmenike
  • 172
  • 1
  • 7
1

My reason for coming here is that i had an outer loop and an inner loop like so:

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

As you can see, it won't actually go to the next x, but will go to the next y instead.

what i found to solve this simply was to run through the array twice instead:

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

I know this was a specific case of OP's question, but I am posting it in the hope that it will help someone think about their problem differently while keeping things simple.

  • This is probably not Python. What is the type of array? Probably list, but what does it contain? Even if it contains ints, array.pop(x) will probably not do what you want. – Veky Aug 10 '16 at 17:07
  • That's a good point. I can't find the code that I referenced. For anyone reading this, array.pop(i) "Removes the item with the index i from the array and returns it." as per python documentation. So one would need to get the index of item x in the array to make this code work as expected. There is also the array.remove(x) function which would do what is expected. I will modify my answer above to fix that error. This assumes the second array contains no duplicates, as array.remove(x) will only remove the first instance of x found. – Nathan Garabedian Apr 09 '17 at 16:38
  • Ok, then I get it. In that case, simply using `break` instead of `continue` would do what you want, wouldn't it? :-) – Veky Apr 10 '17 at 10:22
  • Yeah, for efficiency and clarity, you'd probably want to use break instead of continue in these examples. :) – Nathan Garabedian Jul 03 '17 at 15:35
1

probably little trick like below will do if not prefer to refactorial into function

added 1 break_level variable to control the while loop condition

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
Skycc
  • 3,496
  • 1
  • 12
  • 18
1

You can define a variable( for example break_statement ), then change it to a different value when two-break condition occurs and use it in if statement to break from second loop also.

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
helmsdeep
  • 11
  • 2
  • Good point, however in each of levels above our inner level of interest we would need to scan that variable. Feels really bad that the language does not have a GoTo instruction, performance-wise. – Anatoly Alekseev Dec 12 '18 at 14:36
1

Hopefully this helps:

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
Daniel L.
  • 21
  • 1
1

I came across this recently and, wanting to avoid a duplicate return statement, which can conceal logical errors, looked at @yak's idea. This works well within nested for loops but is not very elegant. An alternative is to check for the condition before the next loop:

b = None
for a in range(10):
    if something(a, b): # should never = True if b is None
        break
    for b in range(20):
        pass

This might not work everywhere but is adaptable and, if required, has the advantage of allowing the condition to be duplicated rather than a potential result.

Charlie Clark
  • 18,477
  • 4
  • 49
  • 55
0

Trying to minimal changes to the OP's question, I just added a flag before breaking the 1st for loop and check that flag on the outer loop to see if we need to brake once again.

break_2 = False
while True:
    # Snip: print out current state
    if break_2: break
    while True:
        ok = get_input("Is this ok? (y/n)")
        if ok.lower() == "y": break_2 = True
        if break_2: break
        if ok.lower() == "n": break
    # Do more processing with menus and stuff
thanos.a
  • 2,246
  • 3
  • 33
  • 29
  • Can you describe what you changed? What is the idea/gist? From [the Help Center](https://stackoverflow.com/help/promotion): *"...always explain why the solution you're presenting is appropriate and how it works"*. Please respond by [editing (changing) your answer](https://stackoverflow.com/posts/70636271/edit), not here in comments (***without*** "Edit:", "Update:", or similar - the answer should appear as if it was written today). – Peter Mortensen May 24 '22 at 11:45
0
while True:
    # Snip: print out current state
    while True:
        ok = get_input("Is this ok? (y/n)")
        if ok.lower() == "y": 
            break_2 = True
        if ok.lower() == "n": 
            break
    if break_2:
        break
Wendel
  • 2,809
  • 29
  • 28
0

If you just need to test an edge case inside a complex nest of for loops, you can throw in a 1/0 to raise an exception. I promise I won't tell anyone. This comes in handy when you quickly want to test a single iteration of a deeply nested for loop, and you don't want to track down a large amount of break statements or comment out a significant amount of code.

Yes, you could wrap it in a function and use return, but in some contexts that can be infeasibly cumbersome.

example for entry level programmers:

for i in first_iter:
    for j in second_iter:
        for k in third_iter:
            print(i_want_to_run_this_once_and_stop_executing(i,j,k))
            1/0
        code_that_takes_a_long_time()
    expensive_code()

in big jupyter notebook scripts that do some heavy data pre-processing, this is especially handy.

Warlax56
  • 1,170
  • 5
  • 30
0

FLAG You can use a flag to break out of the loop:

if found:
  break

Here, 'found' is the flag, you initially set it to False, then in the loop you use this code.

found = False
for table_height in range(500):
  if found:
    break

Here is the full code with three for loops:

found = False
for table_height in range(500):
  if found:
    break
  for cat_height in range(500):
    if found:
      break

    for tort_height in range(500):
      equation1 = table_height + cat_height == tort_height + 170
      equation2 = table_height + tort_height == cat_height + 130
      if equation1 and equation2:
        print('table', table_height, '  cat', cat_height, '  tortoise', tort_height)
        found = True
        break

In this code if equation1 and equation2 are True, it will set the 'found' flag True, and break out of the innermost for loop, and it will also break out of the other two for loops since 'found' is True.

Alper Yilmaz
  • 36
  • 1
  • 6
0

Here is a very short version: Create a file with the name break_out_nested.py

import itertools
import sys

it = sys.modules[__name__] # this allows us to share variables with break_out_nested.py when we import it 


def bol(*args):
    condi = args[-1] # the condition function
    i = args[:-1] # all iterables 
    for p in itertools.product(*i): # itertools.product creates the nested loop
        if condi(): # if the condition is True, we return 
            return
        yield p # if not, we yield the result 

Now you need only a few lines to break out of nested loops (data from Rafiq's example)

from break_out_nested import it, bol # import what we have just created

# you need to create new variables as attributes of it,
# because break_out_nested has only access to these variables
it.i, it.j, it.k = 1, 1, 1
# the break condition
def cond(): return it.i % 3 == 0 and it.j % 3 == 0 and it.k % 3 == 0

# The condition will be checked in each loop 
for it.i, it.j, it.k in bol(range(1, 6, 1), range(1, 11, 2, ), range(1, 21, 4), cond):
    print(it.i, it.j, it.k)

More examples:

def cond(): return it.i + it.j + it.k == 777

it.i, it.j, it.k = 0, 0, 0
for it.i, it.j, it.k in bol(range(100), range(1000), range(10000), cond):
    print(it.i, it.j, it.k)




def cond(): return it.i + it.j + it.k >= 100000

it.i, it.j, it.k = 0, 0, 0
# you dont have to use it.i, it.j, it.k as the loop variables, you can
# use anything you want, but you have to update the variables somewhere
for i, j, k in bol(range(100), range(1000), range(10000), cond):
    it.i, it.j, it.k = i * 10, j * 100, k * 100
    print(it.i, it.j, it.k)
Hans
  • 148
  • 2
  • 7
-1

break for outer and inner while loops:

while True:
    while True:
        print('Breaks inner "while" loop')
        break # Here
    print('Breaks outer "while" loop')
    break # Here

Or, break for outer and inner while loops with if statement:

while True:
    while True:
        if True:
            print('Breaks inner "while" loop')
            break # Here
    print('Breaks outer "while" loop')
    break # Here

Output:

Breaks inner "while" loop
Breaks outer "while" loop

break for outer and inner for loops:

for _ in iter(int, 1):
    for _ in iter(int, 1):
        print('Breaks inner "for" loop')
        break # Here
    print('Breaks outer "for" loop')
    break # Here

Or, break for outer and inner for loops with if statement:

for _ in iter(int, 1):
    for _ in iter(int, 1):
        if True:
            print('Breaks inner "for" loop')
            break # Here
    print('Breaks outer "for" loop')
    break # Here

Output:

Breaks inner "for" loop
Breaks outer "for" loop
Super Kai - Kazuya Ito
  • 22,221
  • 10
  • 124
  • 129
-3

Similar like the one before, but more compact. (Booleans are just numbers)

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   # <--- !
alu5
  • 13
  • 1
  • 2
    This looks pretty ugly and makes the code harder to understand, as compared to the previous one. Also, It's wrong. It misses out on actually checking if the input is acceptable and breaks after 1 loop. – Eric Dec 30 '12 at 16:01
  • Which one before? Does it refer to a particular answer? – Peter Mortensen May 24 '22 at 11:33
-3

Since this question has become a standard question for breaking into a particular loop, I would like to give my answer with example using Exception.

Although there exists no label named breaking of loop in multipally looped construct, we can make use of User-defined Exceptions to break into a particular loop of our choice. Consider the following example where let us print all numbers upto 4 digits in base-6 numbering system:

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

When we print the output, we will never get any value whose unit place is with 4. In that case, we don't break from any loop as BreakLoop(4) is raised and caught in same loop. Similarly, whenever ten place is having 3, we break into third loop using BreakLoop(3). Whenever hundred place is having 5, we break into second loop using BreakLoop(2) and whenver the thousand place is having 2, we break into first loop using BreakLoop(1).

In short, raise your Exception (in-built or user defined) in the inner loops, and catch it in the loop from where you want to resume your control to. If you want to break from all loops, catch the Exception outside all the loops. (I have not shown this case in example).

Prasad
  • 5,946
  • 3
  • 30
  • 36
-4

The way I solve this is by defining a variable that is referenced to determine if you break to the next level or not. In this example, this variable is called 'shouldbreak'.

Variable_That_Counts_To_Three=1
while 1==1:
    shouldbreak='no'
    Variable_That_Counts_To_Five=0
    while 2==2:
        Variable_That_Counts_To_Five+=1
        print(Variable_That_Counts_To_Five)
        if Variable_That_Counts_To_Five == 5:
            if Variable_That_Counts_To_Three == 3:
                shouldbreak='yes'
            break
    print('Three Counter = ' + str(Variable_That_Counts_To_Three))
    Variable_That_Counts_To_Three+=1
    if shouldbreak == 'yes':
        break

print('''
This breaks out of two loops!''')

This gives a lot of control over how exactly you want the program to break, allowing you to choose when you want to break and how many levels to go down.

Erico9001
  • 75
  • 4
  • 4
    Using proper booleans `True` or `False` would at least turn this from horrible to merely unattractive. – tripleee Feb 19 '19 at 05:28