151

What sorts of methods exist for prematurely exiting an if clause?

There are times when I'm writing code and want to put a break statement inside of an if clause, only to remember that those can only be used for loops.

Lets take the following code as an example:

if some_condition:
   ...
   if condition_a:
       # do something
       # and then exit the outer if block
   ...
   if condition_b:
       # do something
       # and then exit the outer if block
   # more code here

I can think of one way to do this: assuming the exit cases happen within nested if statements, wrap the remaining code in a big else block. Example:

if some_condition:
   ...
   if condition_a:
       # do something
       # and then exit the outer if block
   else:
       ...
       if condition_b:
           # do something
           # and then exit the outer if block
       else:
           # more code here

The problem with this is that more exit locations mean more nesting/indented code.

Alternatively, I could write my code to have the if clauses be as small as possible and not require any exits.

Does anyone know of a good/better way to exit an if clause?

If there are any associated else-if and else clauses, I figure that exiting would skip over them.

Roman
  • 3,050
  • 3
  • 21
  • 20
  • 4
    For your second code sample--do you know about `elif`? – Craig McQueen Jan 15 '10 at 05:29
  • 3
    "Alternatively, I could write my code to have the if clauses be as small as possible and not require any exits." -- and surely this would be the best course of action. :-) – Michał Marczyk Jan 15 '10 at 05:31
  • 3
    @Craig McQueen: I do, but say I wanted to have code execute between condition statements? E.g. `if a: #stuff; #stuff_inbetween; if b: #stuff;` The inbetween code depends on `not a` but doesn't depend on `b`. – Roman Jan 15 '10 at 05:38
  • hello please don't forget `elif` https://stackoverflow.com/a/2069680/7045119 – kerbrose Jul 28 '20 at 19:25
  • What is this abomination of a thread filled with april fools' answers and worst practices?? – Toivo Säwén Jul 14 '23 at 13:16

15 Answers15

145

This method works for ifs, multiple nested loops, and other constructs that you can't break from easily.

  1. Wrap the code in its own function.
  2. Instead of break, use return.

Example:

def some_function():
    if condition_a:
        # do something and return early
        ...
        return
    ...
    if condition_b:
        # do something else and return early
        ...
        return
    ...
    return

if outer_condition:
    ...
    some_function()
    ...
Drew Dormann
  • 59,987
  • 13
  • 123
  • 180
  • Oh, I like that idea. It seems like it might have the downside of being slower, though. The PythonWiki performance tips page says "Function call overhead in Python is relatively high" (http://wiki.python.org/moin/PythonSpeed/PerformanceTips) – Roman Jan 15 '10 at 05:28
  • 6
    I'm happy to add to your bag of programmer tricks. In my experience, that approach works nearly every time you're tempted to use a forward-moving goto. (And it both hints at and addresses situations where a single function is getting too big) – Drew Dormann Jan 15 '10 at 05:39
  • 2
    Ideally you can achieve both, but there are times when you must trade good code for good performance. Those times are rare, especially when you are considering using Python. In other words: don't worry so much about function call overhead. – ephemient Jan 15 '10 at 05:47
  • 32
    There's an old anecdote: "Dennis Ritchie encouraged modularity by telling all and sundry that function calls were really, really cheap in C. Everybody started writing small functions and modularizing. Years later we found out that function calls were still expensive on the PDP-11, and VAX code was often spending 50% of its time in the CALLS instruction. Dennis had lied to us! But it was too late; we were all hooked..." – ephemient Jan 15 '10 at 05:48
  • 1
    @ephemient: That's funny, is there more to the story? I'd like to read the whole thing. – Roman Jan 15 '10 at 05:51
  • 6
    This quotation is from chapter 4 of the book The Art of Unix Programming (online at http://www.faqs.org/docs/artu/). You really should read the whole thing, if you haven't before. – ephemient Jan 15 '10 at 05:59
81
from goto import goto, label

if some_condition:
   ...
   if condition_a:
       # do something
       # and then exit the outer if block
       goto .end
   ...
   if condition_b:
       # do something
       # and then exit the outer if block
       goto .end
   # more code here

label .end

(Don't actually use this, please.)

ephemient
  • 198,619
  • 38
  • 280
  • 391
36
while some_condition:
   ...
   if condition_a:
       # do something
       break
   ...
   if condition_b:
       # do something
       break
   # more code here
   break
Thomas Eding
  • 35,312
  • 13
  • 75
  • 106
  • 5
    Oh, hey I really like this idea. I think this exactly solves my original desire. However, I have a nagging feeling that this is not good practice (and for that reason, I'll retain the current accepted answer for now because it promotes good coding style). – Roman Jan 19 '10 at 02:56
  • 9
    Note that you could keep the original if and wrap the entire thing in a `while True:`. Just make sure to put in a `break` statement at the end! For languages with the do-while construct, it is more idomatic to do: `do { code that can conditionally break out } while (false);` – Thomas Eding Jan 19 '10 at 19:07
13

You can emulate goto's functionality with exceptions:

try:
    # blah, blah ...
    # raise MyFunkyException as soon as you want out
except MyFunkyException:
    pass

Disclaimer: I only mean to bring to your attention the possibility of doing things this way, while in no way do I endorse it as reasonable under normal circumstances. As I mentioned in a comment on the question, structuring code so as to avoid Byzantine conditionals in the first place is preferable by far. :-)

Michał Marczyk
  • 83,634
  • 13
  • 201
  • 212
12

may be this?

if some_condition and condition_a:
       # do something
elif some_condition and condition_b:
           # do something
           # and then exit the outer if block
elif some_condition and not condition_b:
           # more code here
else:
     #blah
if
ghostdog74
  • 327,991
  • 56
  • 259
  • 343
  • 1
    Yeah, that could work. I think my mind sort of blanked on `elif` while I was writing this. Although I think this would not work in a situation where I want code to execute in between the nested if statements. – Roman Jan 15 '10 at 05:57
  • 1
    this is actually the correct answer. why I don't see people recommend this?? :) – kerbrose Jul 28 '20 at 19:24
10

For what was actually asked, my approach is to put those ifs inside a one-looped loop

for _ in range(1):
    if (some_condition):
        # do something applicable to all condition
        ...
        if (condition_a):
            # do something
            # and then exit the outer if block
            break
        # do something applicable to some_condition but not for condition_a 
        ...
        if (condition_b):
            # do something
            # and then exit the outer if block
            break
        # do something applicable to some_condition but neither for condition_a/b
        ...

Using this instead of using combination of elif & and, will help you write DRY(don't repeat yourself) code. If you are using elif & and, you might need to rewrite # do something applicable to all condition or other line multiple times. Which is less clean even if the line is just a function call.

Test it:

conditions = [True,False]
some_condition = True

for condition_a in conditions:
    for condition_b in conditions:
        print("\n")
        print("with condition_a", condition_a)
        print("with condition_b", condition_b)
        while (True):
            if (some_condition):
                print("checkpoint 1")
                if (condition_a):
                    # do something
                    # and then exit the outer if block
                    print("checkpoint 2")
                    break
                print ("checkpoint 3")
                if (condition_b):
                    # do something
                    # and then exit the outer if block
                    print("checkpoint 4")
                    break
                print ("checkpoint 5")
                # more code here
            # make sure it is looped once
            break
izzulmakin
  • 559
  • 1
  • 6
  • 17
  • 2
    To be honest, this is the cleanest solution. It's very clear when the code is to exit - you don't have to worry about function scoping inside functions, and there is zero performance or logical debt. – Trent Apr 15 '19 at 23:48
  • 4
    Might consider using `for _ in range(1):` instead of `while True:`. (1) Better communicate your intention of a single iteration loop and (2) no last break statement to exit the loop (might be removed by accident later on) – Bernhard Kausler Sep 11 '19 at 14:25
  • @BernhardKausler I found that independently and seriously considered it because my top-level was "`if verbose`," so `for _ in range(verbose):` worked... but then I was able to refactor it away. – Aaron D. Marasco Oct 18 '22 at 15:00
5

Generally speaking, don't. If you are nesting "ifs" and breaking from them, you are doing it wrong.

However, if you must:

if condition_a:
   def condition_a_fun():
       do_stuff()
       if we_wanna_escape:
           return
   condition_a_fun()
if condition_b:
   def condition_b_fun():
       do_more_stuff()
       if we_wanna_get_out_again:
           return
   condition_b_fun()

Note, the functions don't HAVE to be declared in the if statement, they can be declared in advance ;) This would be a better choice, since it will avoid needing to refactor out an ugly if/then later on.

Enki
  • 304
  • 1
  • 4
  • Thanks for the answer. I'm not sure what you mean by 'nesting loops'. If you are referring to my mention of the keyword 'break', I was simply trying to motivate my search for an if-exit by comparing it to the existence of a loop exit. Also, I am unsure how your code solves the problem, as my example had `if condition_a` and `if condition_b` nested inside an `if some_condition`. I want to be able to break out of the `if some_condition`. – Roman Jan 15 '10 at 18:58
  • The reason people want to nest ifs and breaking them is probably not because they are doing it wrong, but maybe because they want to write clean simple and DRY code. simple case is when the program needs to do x() when both condition_a and condition_b fulfilled, and needs to do y() only for condition_a, and needs to do z() only for condition_b. and the coder refuse to write x() multiple time – izzulmakin Jan 07 '20 at 02:09
5

There is another way which doesn't rely on defining functions (because sometimes that's less readable for small code snippets), doesn't use an extra outer while loop (which might need special appreciation in the comments to even be understandable on first sight), doesn't use goto (...) and most importantly let's you keep your indentation level for the outer if so you don't have to start nesting stuff.

if some_condition:
   ...
   if condition_a:
       # do something
       exit_if=True # and then exit the outer if block
if some condition and not exit_if: # if and only if exit_if wasn't set we want to execute the following code
   # keep doing something
   if condition_b:
       # do something
       exit_if=True # and then exit the outer if block
if some condition and not exit_if:
   # keep doing something

Yes, that also needs a second look for readability, however, if the snippets of code are small this doesn't require to track any while loops that will never repeat and after understanding what the intermediate ifs are for, it's easily readable, all in one place and with the same indentation.

And it should be pretty efficient.

DonQuiKong
  • 413
  • 4
  • 15
4

Here's another way to handle this. It uses a single item for loop that enables you to just use continue. It prevents the unnecessary need to have extra functions for no reason. And additionally eliminates potential infinite while loops.

if something:
    for _ in [0]:
        # Get x
        if not x:
            continue

        # Get y
        if not y:
            continue

        # Get z
        if not z:
            continue

        # Stuff that depends on x, y, and z
Timbot
  • 79
  • 1
  • This might work out well for some situation! However, if the programmer is taking into account Big O notation, this might not be a good solution as it can be expensive to be using for loop. Any thoughts? – stranger Jul 26 '22 at 07:45
  • @stranger that analysis doesn't actually make any sense. Big-O notation isn't simply about the number of nested loops, and simply writing a `for` loop doesn't incur overhead in itself - what matters is the number of times the nested code runs. `for _ in [0]:` loops once, because `[0]` has one item in it. Multiplying the amount of work by 1 doesn't make it bigger. – Karl Knechtel Aug 03 '22 at 02:33
  • As ugly as it is to create blocks artificially like this, I think `for _ in [0]:` is definitely more elegant than using a `while` loop and remembering to `break` it unconditionally at the end. On the other hand, the `while` approach allows `continue` to restart the process in addition to `break` aborting it. – Karl Knechtel Aug 03 '22 at 02:34
  • @KarlKnechtel i agree with you definitely. What if the ```[0]``` gets larger? Would your statement still stands? Pardon my naive thinking of this situation! still learning everyday – stranger Aug 08 '22 at 01:41
  • I don't understand what you mean by "gets larger". If you're asking how to make code loop a set number of times, then the usual Python idiom is to make a `range` and iterate over it. But if you've decided that the code now needs to *loop*, then you're probably going to have to rethink the entire thing anyway. – Karl Knechtel Aug 08 '22 at 01:44
2

Effectively what you're describing are goto statements, which are generally panned pretty heavily. Your second example is far easier to understand.

However, cleaner still would be:

if some_condition:
   ...
   if condition_a:
       your_function1()
   else:
       your_function2()

...

def your_function2():
   if condition_b:
       # do something
       # and then exit the outer if block
   else:
       # more code here
Smashery
  • 57,848
  • 30
  • 97
  • 128
1

So here i understand you're trying to break out of the outer if code block

if some_condition:
    ...
    if condition_a:
       # do something
       # and then exit the outer if block
       ...
    if condition_b:
       # do something
       # and then exit the outer if block
# more code here

One way out of this is that you can test for for a false condition in the outer if block, which will then implicitly exit out of the code block, you then use an else block to nest the other ifs to do something

if test_for_false:
    # Exit the code(which is the outer if code)

else:
    if condition_a:
        # Do something

    if condition_b:
        # Do something
Romeo
  • 740
  • 1
  • 5
  • 21
1

The only thing that would apply this without additional methods is elif as the following example

a = ['yearly', 'monthly', 'quartly', 'semiannual', 'monthly', 'quartly', 'semiannual', 'yearly']
# start the condition
if 'monthly' in b: 
    print('monthly') 
elif 'quartly' in b: 
    print('quartly') 
elif 'semiannual' in b: 
    print('semiannual') 
elif 'yearly' in b: 
    print('yearly') 
else: 
    print('final') 
kerbrose
  • 1,095
  • 2
  • 11
  • 22
1

There are several ways to do it. It depends on how the code is implemented.

If you exit from a function, then use return; no code will be executed after the return keyword line. Then the example code is:

def func1(a):
    if a > 100:
        # some code, what you want to do
        return a*a 
    if a < 100:
         # some code, what you want to do
        return a-50
    if a == 100:
         # some code, what you want to do
        return a+a

If you exit from a loop, use break. No code will be executed after break keyword. Then the example code is, for while and for loops:

a = 1
while (True):
    if (a == 10):
        # some code, what you want to do
        break
    else:
        a=a+1
        print("I am number", a)

for i in range(5):
    if i == 3:
        break
    print(i)

If you exit from the basic conditional, then you can use the exit() command directly. Then code after the exit() command will not be executed.

NB: This type of code is not preferable. You can use a function instead of this. But I just share the code for example.

The example code is:

if '3K' in FILE_NAME:
        print("This is MODIS 3KM file")
        SDS_NAME = "Optical_Depth_Land_And_Ocean"
        
    elif 'L2' in FILE_NAME:
        print("This is MODIS 10KM file")
        SDS_NAME = "AOD_550_Dark_Target_Deep_Blue_Combined" 
        exit()
    else:
        print("It is not valid MODIS file")
Toby Speight
  • 27,591
  • 48
  • 66
  • 103
MD. SHIFULLAH
  • 913
  • 10
  • 16
0

I scrolled through, and nobody mentioned this technique. It's straightforward compared to other solutions and if you do not want to use a try-except statement, this may be your best option.

#!/usr/bin/python3
import sys

foo = 56

if (foo != 67):
    print("ERROR: Invalid value for foo")
    sys.exit(1)
Sten Techy
  • 79
  • 1
  • 7
-2

use return in the if condition will returns you out from the function, so that you can use return to break the the if condition.

Nikhil Parashar
  • 431
  • 5
  • 11