2

Given this simple condition:

if x == y or x == z:
    print("Hello World!");

I understand that Python would first look to see if x is equal to y and if x is not equal to y it then it would check to see if x is equal to z, printing Hello World! if at least one of the conditions is True.

If I were to do this instead:

if x in (y, z):
    print("Hello World!");

To my understanding Python would iterate through the "yz" tuple and then print Hello World! if the value of x is in the "yz" tuple.

Which method would be faster / more efficient to use?
Would Python not bother to check if x was equal to z if x was equal to y?
Would Python still execute the code in the if statement if x was equal to y but not z?

Thank you in advance.

Alex Bowe
  • 523
  • 3
  • 11
  • 1
    You could always do benchmark tests for the first question. Not sure about second question. You could test for the third question. This [answer (and some comments there)](http://stackoverflow.com/a/15112149/2498729) talk about efficiency. Sets are an option too `x in {y, z}`. – But I'm Not A Wrapper Class Jun 06 '16 at 12:53
  • 1
    I can't imagine that it would make any real difference. If it does, you should probably be using C. – John Coleman Jun 06 '16 at 12:54

5 Answers5

2

Let's test it out ourselves.

Here is a class that overloads the equality operator to let us see what Python is doing:

class Foo:
  def __init__(self, name):
    self.name = name

  def __eq__(self, other):
    print self.name, "==", other.name, "?"
    return self.name == other.name

Let's test out short circuiting:

# x and a are passed the same string because we want x == a to be True
x = Foo("a")
a, b = Foo("a"), Foo("b")
if x in (a, b):
  print "Hello World!"

For me, this outputs:

a == a ?
Hello World!

a == b ? was not printed, so short-circuiting does work as desired. The block is also executed as desired.

Now for speed. If we modify the above __eq__ method to remove the print statement (to avoid I/O in our benchmark) and use IPython's %timeit magic command, we can test it this way:

c = Foo("c") # for comparison when x is not equal to either case
%timeit x in (a, b) # equal to first case
%timeit (x == a or x == b)
%timeit x in (b, a) # equal to second case
%timeit (x == b or x == a)
%timeit x in (b, c) # not equal to either case
%timeit (x == b or x == c)

These are the average times per iteration (from 1 million iterations):

Code               Time (ns)
x in (a, b)        437
x == a or x == b   397
x in (b, a)        796
x == b or x == a   819
x in (b, c)        779
x == b or x == c   787

So, pretty comparable results. There is a slight difference, but it isn't big enough to worry about. Just use whichever is most readable in a case-by-case basis.

Alex Bowe
  • 523
  • 3
  • 11
  • `x, y, z = X("x"),` **`X("x"),`** `X("z")` do you maybe mean to make the middle one `X("y")`? – Tadhg McDonald-Jensen Jun 06 '16 at 13:31
  • @TadhgMcDonald-Jensen no, because I wanted the equality operator to evaluate to `True` for that case. Maybe the class name and string value is a little confusing. For now I'll add a comment to explain it. – Alex Bowe Jun 06 '16 at 13:34
  • I'd just use `a` and `b` as the string if they do not directly represent the variable names, because yes it is really confusing. Also tell me, did you remove the print statement in the `__eq__` before timing it? – Tadhg McDonald-Jensen Jun 06 '16 at 13:39
  • @TadhgMcDonald-Jensen I agree. I'll update the string values to those. Thanks. And yes, I removed the print statement (I mention it before the `%timeit` commands). – Alex Bowe Jun 06 '16 at 13:41
  • Thank you for your answer & the tests, appreciate it :) –  Jun 06 '16 at 18:22
1

For two choices, the first is more readable, for twenty, the second one. Performance is not a real issue.

Peter Krassoi
  • 571
  • 3
  • 11
1

When there are only two options this is really just opinion based, although I'd like to point out that if you need to check x against say 5 values there would be a more substantial difference:

if x == a or x == b or x == c or x == d or x == e:

vs:

if x in (a,b,c,d,e):

Especially if you need to later change it to not in vs all of the == to != the second seems easier to expand upon.

Tadhg McDonald-Jensen
  • 20,699
  • 5
  • 35
  • 59
0

I'd plump for if x == y or x == z:

  1. You can exploit the fact that x == z will not be evaluated if x == y.

  2. Creating the anonymous temporary tuple is potentially an overhead.

  3. if x == y or x == z is common in other languages too and so perhaps is more readable.

Perhaps though a good python interpreter can optimise out the anonymous temporary.

Bathsheba
  • 231,907
  • 34
  • 361
  • 483
0

I know that python is a lazy langage, he won't check the second statement if he don't need to. But i don't know if your other method would be faster.

Would Python not bother to check if x was equal to z if x was equal to y?

Yes, Python would not check it.

Would Python still execute the code in the if statement if x was equal to y but not z

No, he won't.

Omegaspard
  • 1,828
  • 2
  • 24
  • 52