TimothyAWiseman
TimothyAWiseman

Reputation: 14883

Python efficiency of and vs multiple ifs

Is there an efficiency difference between using and in an if statement and using multiple if statements? In other words, is something like

if expr1 == expr2 and expr3==expr4:
  dostuff()

different from an efficiency standpoint then:

if expr1 == expr2:
  if expr3 == expr4:
    dostuff()

My very basic testing does not reveal a difference, but does someone with more knowledge (or at least more thorough testing) have a definitive answer?

Upvotes: 22

Views: 11976

Answers (5)

Matthew J Morrison
Matthew J Morrison

Reputation: 4403

In either case, expr1 == expr2 evaluates to false in if, the second will not be evaluated.

Upvotes: 7

Daniel Kluev
Daniel Kluev

Reputation: 11325

When in doubt, you can check what does python compile your statements in, using dis module:

>>> import dis
>>> def test1():
...     if expr1 == expr2 and expr3==expr4:
...        dostuff()
... 
>>> def test2():
...     if expr1 == expr2:
...        if expr3 == expr4:
...           dostuff()
... 
>>> dis.dis(test1)
  2           0 LOAD_GLOBAL              0 (expr1)
              3 LOAD_GLOBAL              1 (expr2)
              6 COMPARE_OP               2 (==)
              9 JUMP_IF_FALSE           24 (to 36)
             12 POP_TOP             
             13 LOAD_GLOBAL              2 (expr3)
             16 LOAD_GLOBAL              3 (expr4)
             19 COMPARE_OP               2 (==)
             22 JUMP_IF_FALSE           11 (to 36)
             25 POP_TOP             

  3          26 LOAD_GLOBAL              4 (dostuff)
             29 CALL_FUNCTION            0
             32 POP_TOP             
             33 JUMP_FORWARD             1 (to 37)
        >>   36 POP_TOP             
        >>   37 LOAD_CONST               0 (None)
             40 RETURN_VALUE        
>>> dis.dis(test2)
  2           0 LOAD_GLOBAL              0 (expr1)
              3 LOAD_GLOBAL              1 (expr2)
              6 COMPARE_OP               2 (==)
              9 JUMP_IF_FALSE           28 (to 40)
             12 POP_TOP             

  3          13 LOAD_GLOBAL              2 (expr3)
             16 LOAD_GLOBAL              3 (expr4)
             19 COMPARE_OP               2 (==)
             22 JUMP_IF_FALSE           11 (to 36)
             25 POP_TOP             

  4          26 LOAD_GLOBAL              4 (dostuff)
             29 CALL_FUNCTION            0
             32 POP_TOP             
             33 JUMP_ABSOLUTE           41
        >>   36 POP_TOP             
             37 JUMP_FORWARD             1 (to 41)
        >>   40 POP_TOP             
        >>   41 LOAD_CONST               0 (None)
             44 RETURN_VALUE        

So as you can see, at python bytecode level, both statements are same - even while you use single if at first statement, it will do JUMP_IF_FALSE after first comparison.

Upvotes: 5

John Machin
John Machin

Reputation: 83032

Any differences in speed between using and and nested ifs will be minimal. You are barking up the wrong tree. Consider this tree:

if oftenTrueCondition and rarelyTrueCondition:

compared with

if rarelyTrueCondition and oftenTrueCondition:

So, unless the first condition must be evaluated first (it is a guard to stop the next expression from crashing or doing something silly/expensive), consider swapping the order of evaluation.

Upvotes: 18

leoluk
leoluk

Reputation: 12981

The first one (one if with and) is faster :-)

I tried it out using timeit. These are the results:

Variant 1: 9.82836714316
Variant 2: 9.83886494559
Variant 1 (True): 9.66493159804
Variant 2 (True): 10.0392633241

For the last two, the first comparision is True, so the second one is skipped. Interesting results.


import timeit


print "Variant 1: %s" % timeit.timeit("""
for i in xrange(1000):
    if i == 2*i and i == 3*i:
        pass
        """,
        number = 1000)

print "Variant 2: %s" % timeit.timeit("""
for i in xrange(1000):
    if i == 2*i:
        if i == 3*i:
            pass
        """,
        number = 1000)

print "Variant 1 (True): %s" % timeit.timeit("""
for i in xrange(1000):
    if i == i and i == 3*i:
        pass
        """,
        number = 1000)

print "Variant 2 (True): %s" % timeit.timeit("""
for i in xrange(1000):
    if i == i:
        if i == 3*i:
            pass
        """,
        number = 1000)

Upvotes: 3

froadie
froadie

Reputation: 83183

This isn't enough of a performance difference, if any, to affect your decision. IMO, the decision here should be made purely from a readability perspective. The first is generally more standard, I think, but there are situations when the second might be clearer. Choose the method that best gets your intent across.

Upvotes: 14

Related Questions