8

In C there's a clever trick that lets you avoid pyramid-style code by turning:

if (check1())
  if (check2())
    if (check3())
      do_something();

into:

do {
  if (!check1())
    break;

  if (!check2())
    break;

  if (!check3())
    break;

  do_something();
} while (0);

What's the cleanest way for me to do this in Python, which doesn't have a do-while construct?

Note: I'm not necessarily asking for a way to implement a do-while loop in Python, but a technique to avoid the aforementioned pyramid-style code.

Update: It seems there's some confusion. The only reason I'm using a loop is to be able to break out at any point in the body, which is only supposed to be executed once.

Essentially what I'm doing in Python is this:

while True:
    if not check1():
        break

    if not check2():
        break

    if not check3():
        break

    do_domething()
    break

I'm just wondering if there's a cleaner way.

8 Answers8

7

The Pythonic way of writing this would be

if check1() and check2() and check3():
    do_something()

In Python we emphasize clarity and simplicity of the code, not using clever programming tricks.


[Edit] If you need to "create a variable and use it in the first check", then you would use the pyramid-style:

if check1():
    #variables and stuff here
    if check2():
        #variables and stuff here
        if check3():
            doSomething()

Or, as @Blender suggests, refactor it into a separate method. These are all much simpler and clearer ways of communicating your intent than using a loop that's not intended to loop.

BlueRaja - Danny Pflughoeft
  • 84,206
  • 33
  • 197
  • 283
  • I like this solution, but sometimes it's not as simple as a function call. For example, I may have to create a variable and use it in the first check, which would take two lines of code. –  Jul 19 '13 at 18:34
  • @Brandon: Then you would write it using your "Pyramid style code." It may have more indentation, but it is still the simplest and clearest way to write it. – BlueRaja - Danny Pflughoeft Jul 19 '13 at 18:36
  • 1
    *In Python we emphasize clarity and simplicity of the code, not using clever programming tricks.* While this is a nice sentiment, it is not a logical argument. Clarity and simplicity are subjective notions that can change over time. And just because someone (or even many) say some way of programming is clear and simple will not make it universally true for everyone. – jxh Jul 19 '13 at 21:31
  • 2
    @jxh: If you never expect to maintain your code, sure, do whatever you want. When Brandon posted the original C code, just about everyone misinterpreted it. That's not a sign of good code, regardless of your language. – Blender Jul 20 '13 at 07:12
  • @Blender: I would never say to write bad code. But any idiom can be considered clear and simple to the people that use it. That doesn't mean it's so for anybody else. – jxh Jul 20 '13 at 18:17
  • 1
    @jhx: that's what writing _idiomatic_ code is all about: using idioms that are considered "clear and simple" _in the language you are using_ (that is, for the majority of this language's users). FWIW I would not label the original C snippet it as "idiomatic C" as far as I'm concerned. – bruno desthuilliers Aug 14 '13 at 10:25
  • The example used 3 checks. If it instead have 10 checks and each check takes multiple lines and action takes multiple lines, with all the levels of indents, it will look ugly regardless of programming language. The intention of Brandon's code is clear to me. Branch off early and don't bother reading the rest. – some user Apr 29 '16 at 00:39
5

Invert your conditions and break out early. If you structure your code well, you won't have to worry about if staircases in the first place:

def do_bigger_something():
    if not check1():
        return

    if not check2():
        return

    if not check3():
        return

    do_something()

There's a good chance that if one portion of your code does a lot of these checks, it should be turned into a function anyways.

Blender
  • 289,723
  • 53
  • 439
  • 496
2
if (check1() and check2() and check3()):
    do_something()
inspectorG4dget
  • 110,290
  • 27
  • 149
  • 241
1
  if check1() and  check2() and  check3():  
         do_something()
Jon Kiparsky
  • 7,499
  • 2
  • 23
  • 38
1

I'd say if the checks are not as trivial as this (which can be done with a simple and), refactor your code and break it out into a function instead of mis-using a loop for things it's not meant to do;

def doSomethingIfConditions():

  if not check1():
    return

  if not check2():
    return

  if not check3():
    return

  doSomething()

...your code...
doSomethingOnConditions()
...your code...
Joachim Isaksson
  • 176,943
  • 25
  • 281
  • 294
0

Solution #1 (straightforward):

while True:
    if not check1():
        break
    if not check2():
        break
    if not check3():
        break
    do_something()
    break

Solution #2 (more pythonic):

for check in check1, check2, check3:
    if not check():
        break
else:
    # the else part is only executed
    # if we didn't break out the loop
    do_something()

Solution #3 (even more pythonic):

if all(c() for c in check1,check2, check3):
    # all(seq) will stop at the first false result
    do_something()
bruno desthuilliers
  • 75,974
  • 6
  • 88
  • 118
  • Is there an indent problem, or is an `else` allowed as part of a `for` loop (instead an `if`)? – Jonathan Leffler Jul 19 '13 at 19:40
  • @JonathanLeffler : no indentation problem, the `for` statement has an optional `else` block that is only executed if you don't break out of the loop. This is documented here : http://docs.python.org/2/reference/compound_stmts.html#the-for-statement – bruno desthuilliers Aug 14 '13 at 10:18
0

I do not see the need for a loop... You break out of it at the end anyways.

if all((check1(), check2(), check3())):
    print "Do something"

Or just go with the block-style if it's needed.

b0bz
  • 2,140
  • 1
  • 27
  • 27
-2

In C, the use of the do ... while(0) construct that you have shown is usually used when the C programmer wants something that behaves like a goto, but using an actual goto is out of the question (for various reasons). So, the break out of the do ... while(0) is really a kind of hack. Using the same idiom in Python would be perpetuating that hack.

In C, I would generally avoid using this particular use of do ... while(0), and instead opt for a checking function. In Python, this would be:

def do_checks():
    if not check1():
        return False
    if not check2():
        return False
    if not check3():
        return False
    return True

if do_checks():
    do_something()

Probably the most direct translation of C's do ... while(0) construct would be a loop with a single iteration. Don't do this.

for x in range(1):
    if not check1():
        break
    if not check2():
        break
    if not check3():
        break
    do_something()
jxh
  • 69,070
  • 8
  • 110
  • 193
  • Accepted as in my opinion this most closely mimics the `do { ... } while(0)` technique I mentioned. –  Jul 19 '13 at 18:36
  • 1
    -1 You're not writing Python, you're writing [C-code in Python](http://programmers.stackexchange.com/questions/96524/96736#96736). – BlueRaja - Danny Pflughoeft Jul 19 '13 at 18:40
  • 1
    Yes, this is horrible, this idea. – kindall Jul 19 '13 at 18:50
  • Since the author has updated his answer to emphasize that the answer to the original question should not be used, I have re-accepted his answer as it most closely mimicked what I was asking. –  Sep 08 '13 at 13:46