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.
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.
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.
if (check1() and check2() and check3()):
do_something()
if check1() and check2() and check3():
do_something()
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...
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()
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.
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()
Related
Our code reviewer is telling me that flag in the code below is bad and I need to refactor code in a "Python-like" style. I really can't understand how I can avoid this flag usage, here is a sample of code:
flag = false
for (i in range(2,4)):
zoom()
if (check_condition()):
flag = true
break
if (flag):
tap()
else:
raise Exception("failed")
I can see the only way to make a tap inside if. In this case I will have to add a return statement inside a loop, which is not the best idea from the code style point of view, isn't it?
So maybe someone can suggest a better way to organize this code?
You can use an else: statement with the for statement. It's only executed if the loop finishes normally, instead of exiting with break
for i in range(2, 4):
zoom()
if check_condition():
break
else:
raise Exception("failed")
tap()
I don't think you need the falg here either, if you just call the tab() method instead of changing the flag value to true. It will work the same.
for (i in range(2,4)):
zoom()
if (check_condition()):
tab()
break
else:
raise Exception("failed")
Suppose i have below statement :
for i in range(10000):
if i==5:
do_something_for_5()
else:
do_something()
so you can see python need to check 10000 times whether i equals to 5, and 9999 of checking is wasted. my question is, is there any trick that after catching 5 once, python will bypass if-checking and go directly to exec do_something()? somewhat like short-circuit the whole if checking. How to do that?
More..
I don't think it is python problem, you see such checking pattern frequently in many languages.(compilers will optimize it?) The case is just for demo, I just hate to see the computer to check and check and check fruitless and just want to know the trick to avoid this. It likes such scenario: you know there is only one bad guy, and once you catch the bad guy, you withdraw the policemen and security checking to let others go directly, that will let process run faster.
More general code like this:
for member in member_list:
if time_consuming_inspect(member)==special_character:#known there is only one candidate match in advance
do_special_thing(member)
else:
do_common_thing(member)
Do a straightforward thing: break out of the loop after seeing 5, and run another loop to finish the work:
for i in range(10000):
if i==5:
do_something_for_5()
break
else:
do_something()
for i in range(i+1, 10000):
do_something()
One way would be to write something like this instead:
for i in range(4):
do_something()
do_something_for_5()
for i in range(6, 10000):
do_something()
If the special case for 5 need not be executed in order, then this will reduce duplication:
for i in itertools.chain(range(5), range(6, 10000)):
print i
It sounds like you really want something like self-modifying code so that it never does the check again, but I would advise against that. There isn't much impact for an extra check.
Response to comment
The following is not recommended and is overkill for this example, but it does work. It uses a different function after the check for 5 has succeeded. Keep in mind the function that the variable is assigned to must be looked up, so I doubt there will be a speed increase. This probably has very limited use, but nevertheless:
do_something = None
def do_something_with_check(i):
if i != 5:
print 'Doing something with check.'
else:
do_something_for_5()
global so_something
do_something = do_something_without_check
def do_something_without_check(i):
print 'Doing something without check.'
def do_something_for_5():
print 'Doing something for 5.'
do_something = do_something_with_check
for i in range(10):
do_something(i)
prints out:
Doing something with check.
Doing something with check.
Doing something with check.
Doing something with check.
Doing something with check.
Doing something for 5.
Doing something without check.
Doing something without check.
Doing something without check.
Doing something without check.
seenFive = False
for i in range(10000):
if not seenFive and i==5:
do_something_for_5()
seenFive = True
else:
do_something()
You could not iterate over 5, and perform that function separately:
nums = range(10000) # list(range(10000)) on python3
nums.remove(5) # Get rid of 5 from the list.
do_something_for_5()
for i in nums:
do_something()
I'm not sure this will help since the overhead of making the list might be more than checking if i==5 in each iteration.
I find myself using this code pattern quite a bit, and every time I do I think there might be a better, clearer way of expressing myself:
do_something = True
# Check a lot of stuff / loops
for thing in things:
....
if (thing == 'something'):
do_something = False
break
if (do_something):
# Do something
So essentially, "plan on doing something but if this particular condition is found anytime, anywhere, don't do it"
Maybe this code is perfectly fine, but I wanted to see if anyone had a better suggestion.
Thanks for any input
Python for loops can have an else block, which is executed if those loop is not broken out of:
for thing in things:
...
if (thing == 'something'):
break
else:
... # Do something
This code will work in the same way as yours, but doesn't need a flag. I think this fits your criteria for something a bit more elegant.
Is while True an accepted method for looping over a block of code until an accepted case is reached as below? Is there a more elegant way to do this?
while True:
value = input()
if value == condition:
break
else:
pass
# Continue code here.
Thank you for any input.
That's the way to do this in Python. You don't need the else: pass bit though.
Note, that in python 2.x you're likely to want raw_input rather than input.
If it's deterministic, then yes. If it is not deterministic (thus meaning you could be stuck in a loop forever at some statistical likelihood) then no.
If you wanted to make it a little more clean and easier to debug as the code grows larger, use a boolean or integer to indicate the status of your loop condition.
in the following python code:
narg=len(sys.argv)
print "#length arg= ", narg
if narg == 1:
print "#Usage: input_filename nelements nintervals"
break
I get:
SyntaxError: 'break' outside loop
Why?
Because break cannot be used to break out of an if - it can only break out of loops. That's the way Python (and most other languages) are specified to behave.
What are you trying to do? Perhaps you should use sys.exit() or return instead?
break breaks out of a loop, not an if statement, as others have pointed out. The motivation for this isn't too hard to see; think about code like
for item in some_iterable:
...
if break_condition():
break
The break would be pretty useless if it terminated the if block rather than terminated the loop -- terminating a loop conditionally is the exact thing break is used for.
Because break can only be used inside a loop.
It is used to break out of a loop (stop the loop).
Because the break statement is intended to break out of loops. You don't need to break out of an if statement - it just ends at the end.
This is an old question, but if you wanted to break out of an if statement, you could do:
while 1:
if blah:
break