tags:

views:

79

answers:

4

I'm writing an if statement in Python with a lot of OR conditions. Is there an elegant way to do this with a list, within in the condition rather than looping through the list?

In other words, what would be prettier than the following:

if foo == 'a' or foo == 'b' or foo == 'c' or foo == 'd':

I've just taken up Python, and the language has me wanting to write better.

+2  A: 
if foo in ("a", "b", "c", "d"):

in, of course, works for most containers. You can check if a string contains a substring ("foo" in "foobar"), or if a dict contains a certain key ("a" in {"a": "b"}), or many things like that.

Matti Virkkunen
beat me by 1 second!
Mike Axiak
@Mike: I forgot the `:` though, that might've made the difference!
Matti Virkkunen
+7  A: 
if foo in ('a', 'b', 'c', 'd'):
    #...

I will also note that your answer is wrong for several reasons:

  1. You should remove parentheses.. python does need the outer ones and it takes room.
  2. You're using an assignment operator, not an equality operator (=, not ==)
  3. What you meant to write as foo == 'a' or foo == 'b' or ..., what you wrote wasn't quite correct.
Mike Axiak
Thank you! Also, you're very right about my mistake. The assignment operator was an honest mistake, the others are good to know.
Peter O
I completely disagree with `#1`. Removing parenthesis because it takes up 2 characters is NOT a good idea. If it makes it clearer to read, do it
Falmarri
@Falmarri - The added characters are unneeded noise that decrease readability. I have yet to see an if (...): that is easier to read because of the parentheses
Mike Axiak
`if not (a or not b) and a or a and not (not b or a):` I for one would prefer if there was some parenthesis in there so it's clear when scrolling down through hundreds of lines of code. Also, let's not forget about complicated ternary operations `a = b if a or b and not c else c if a and b else d` not that I'm recommending doing that.
Falmarri
In neither of those cases do I think adding parentheses is the solution ;)
Mike Axiak
+1  A: 
>>> foo = 6
>>> my_list = list(range(10))
>>> my_list
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> print any(foo == x for x in my_list)
True
>>> my_list = list(range(0))
>>>  print any(foo == x for x in my_list)
False

Alternatively:

>>> foo = 6
>>> my_list = set(range(10))
>>> foo in my_list
True
>>> my_list = set(range(0))
>>> foo in my_list
False
hughdbrown
+2  A: 
checking_set = set(("a", "b", "c", "d")) # initialisation; do this once

if foo in checking_set: # when you need it

Advantages: (1) give the set of allowable values a name (2) may be faster if the number of entries is large

Edit some timings in response to "usually much slower" when only "a handful of entries" comment:

>python -mtimeit -s"ctnr=('a','b','c','d')" "'a' in ctnr"
10000000 loops, best of 3: 0.148 usec per loop

>python -mtimeit -s"ctnr=('a','b','c','d')" "'d' in ctnr"
1000000 loops, best of 3: 0.249 usec per loop

>python -mtimeit -s"ctnr=('a','b','c','d')" "'x' in ctnr"
1000000 loops, best of 3: 0.29 usec per loop

>python -mtimeit -s"ctnr=set(('a','b','c','d'))" "'a' in ctnr"
10000000 loops, best of 3: 0.157 usec per loop

>python -mtimeit -s"ctnr=set(('a','b','c','d'))" "'d' in ctnr"
10000000 loops, best of 3: 0.158 usec per loop

>python -mtimeit -s"ctnr=set(('a','b','c','d'))" "'x' in ctnr"
10000000 loops, best of 3: 0.159 usec per loop

(Python 2.7, Windows XP)

John Machin
of course it's usually much slower when the number of items is a handful
Mike Axiak
@Mike Axiak: I've edited my answer to show some timings ... what's the basis for your assertion?
John Machin
Strangely enough, the new set syntax in python 2.7 "ctnr={'a','b','c','d'}" appears to be slightly slower than the old syntax on my Windows XP box.
Wang Dingwei
@Wang Dingwei: True, when the items are constant so that the compiler can emit a constant tuple for the "old" syntax, and not just on your Windows XP box. Not strange at all when you consider what each needs to do. Try this: `import dis; f1=lambda:set(('a','b','c','d')); f2=lambda:{'a','b','c','d'}; dis.dis(f1); dis.dis(f2)`. However this difference is irrelevant if the set is constructed ONCE and accessed many times.
John Machin