Whenever I'm coding something that requires a lot of conditionals, I end up doing this:
if foo:
if bar:
if foobar:
if barfoo:
if foobarfoo:
if barfoobar:
# And forever and ever and ever
I can't write if foo and bar and foobar and ...
because I check for the value list elements (if foo[1] == 'bar'
) inside of an if
somewhere down the line, and if the list index don't exist, I get an error.
Is there a shortcut to conditionally checking things like this, or an alternative method? Thanks.
7 Answers 7
I can't write if foo and bar and foobar and ... because I call list elements inside of an if somewhere down the line, and if the list index don't exist, I get an error.
in python,and
short circuits. If the left side of the expression is false, the right side is not evaluated at all.
foo = dict()
if 'bar' in foo and foo['bar']:
doSomething()
-
I didn't know that Python short-circuited. That makes life a lot easier.– BlenderCommented Feb 12, 2011 at 2:03
-
@Blender: then probably you'll have to mark TokenMacGuy's answer as the answer, since the currently selected Lacrymology's one does not help at all with short circuiting.– tzotCommented Oct 15, 2011 at 9:28
-
@tzot: my answer was previously accepted. I suppose op finds Lycramology's answer more stimulating. Commented Oct 15, 2011 at 14:25
Fail fast:
if not foo:
return
if not foobar:
return
and so forth.
There's also
if all((foo, bar, foobar, barfoo, foobarfoo, barfoobar)):
print "oh yeah"
all will also shortcircuit
-
Now this is more like it. A bit late to answer, but this is much easier. Thanks!– BlenderCommented Sep 28, 2011 at 15:21
-
no problem, you can also do if all([obj.property for obj in obj_list]) for example Commented Oct 4, 2011 at 16:53
Forgive me if I'm stating the obvious -- but if you're checking for many different conditions in advance of one or two operations, you might be better off using try/except
-- especially for those conditions (if any) that are clear error conditions.
-
1Especially if those conditions may change (perhaps by external factors) by the time you have finished all of your checks. More so if the usual case is for all to succeed:
try:except:
is fast, unless you actually hit an exception. Commented Feb 12, 2011 at 8:37
See if you can't break some of that out into a function that includes some of the conditionals (assuming some are in common with your various cases).
-
@wheaties: This response attempts to solve the problem posed in the original question. There's no reason for it to be a comment. comments are not solutions to the original problem. Commented Feb 12, 2011 at 4:19
Break it up into several sub-components where appropriate. As for where to draw the dividing lines, that's really up to you. While a huge staircase of conditional statements isn't great, neither is a massive if-statement with so many predicates that they wrap several lines. Instead, try to group your conditions into logical bunches.
You might write it as:
if foo and bar and foobar:
...
if barfoo and foobarfoo and barfoobar:
...
I also suggest introducing helper methods along the way. Even if those helper methods are called only from this code, that's fine.
def handle_bar():
if barfoo and foobarfoo and barfoobar:
...
if foo and bar and foobar:
...
handle_bar()
If scopes get confusing or you find yourself passing around too much state as function arguments, wrap it in a class and use member variables where its conceptually appropriate.
Overall, my advice is to separate concepts into individual pieces of code at an appropriate granularity. If you don't do it at all, you get a long piece of code that requires lots of scrolling to see the big picture. If you over-do it, you force the reader to jump around your code too much.
If you have more than 3 to 5 tests (or more), consider keeping your conditions in a dictionary, list or tuple. Then test that data structure. Much cleaner than many individual named data.
If you are testing "truth" against a named list of variables of unknown length or a sequence data structure (like a list or tuple) you can do this:
def all_true(*args):
for test in args:
if bool(test) is False: return False
return True
foo=bar=foobar=barfoo=foobarfoo=barfoobar=1
if foo:
if bar:
if foobar:
if barfoo:
if foobarfoo:
if barfoobar:
print "True by Stairs!"
if all_true(foo,bar,foobar,barfoo,foobarfoo,barfoobar):
print "True by function!"
t=(foo,bar,foobar,barfoo,foobarfoo,barfoobar)
if all_true(*t): print "The tuple is true!"
l=[foo,bar,foobar,barfoo,foobarfoo,barfoobar]
if all_true(*l): print "list is true!"
bar=0
# run the same tests...
The all_true()
function will short-circuit against the first false it finds.
if
?"and/or
are short-circuited.