tags:

views:

137

answers:

6

I have a variable s which contains a one letter string

s = 'a'

Depending on the value of that variable, I want to return different things. So far I am doing something along the lines of this:

if s == 'a' or s == 'b':
   return 1
elif s == 'c' or s == 'd':
   return 2
else: 
   return 3

Is there a better way to write this? a more pythonic way? Or is this the most efficient?

Previously, I incorrectly had something like this:

if s == 'a' or 'b':
   ...

Obviously that doesn't work and was pretty dumb of me.

I know of conditional assignment and have tried this:

return 1 if s == 'a' or s == 'b' ...

I guess my question is specifically to is there a way you can compare a variable to two values without having to type 'something == something OR something == something'

+12  A: 
if s in ('a', 'b'):
  return 1
elif s in ('c', 'd'):
  return 2
else:
  return 3

Updated, commenter is right that these should be tuples, not lists.

Jesse Dhillon
wow so simple. thanks
pythonrubies
@pythonrubies - if this is what you were looking for, accept it as the answer. @Jesse - I would recommend using tuples instead of lists for your code snippet.
Matthew J Morrison
Quick question, is there any difference between @Jesse Dhillon answer and @Tim Pietzcker?
pythonrubies
Tim's will only work for strings, this will work for any object that you can test equality against.
Daenyth
Just out of curiousity, does anyone know if this actually speeds up the process at all? I mean, does it save processor time, or just programmer time?
Shaded
@pythonrubies: Tim's answer would also return `1` or `2` if `s` was `ab` or `cd`.
Jesse Dhillon
@Shaded: The construction of the list may actually make it slightly slower than the two separate tests. In practice it will probably be an insignificant difference. You should profile the code rather than trying to guess if performance is a real concern.
Mark Byers
@Shaded Did a quick timeit test. if-or is 5.57 usec/func and if-in is 5.22 usec/func (over 10M iterations).
shookster
@Mark Byers: I was just curious, I don't work with Python but just from a low level perspective it seems like it would add a few extra steps for the processor, however negligible it might be :) Thanks for the response!
Shaded
@shookster: Wow! I would not have expected that, thanks for taking the time to do some tests!
Shaded
It seems it is faster then comparing them separetely? from @shookster's tests. Also, would using the conditional assignment be more 'pythonic' then writing out the if-else?
pythonrubies
@Jesse, @Matthew: why are tuples better than lists for this? Won't they both work?
Dan
@Dan, two reasons. First, tuples are immutable so semantically it makes sense to use them for values that don't change. In this case, you will always be comparing against `a, b, c, d` and those values are known at compile time, so the semantically correct solution would be to use an immutable type. Second, related to the first, there is a performance/efficiency boost when you use tuples, and I don't remember the details but you would expect tuples to use less memory and instantiate faster.
Jesse Dhillon
@Jesse: thanks for the explanation! :)
Dan
+1  A: 

If you only return fixed values, a dictionary is probably the best approach.

Fabian
+1  A: 
if s in 'ab':
    return 1
elif s in 'cd':
    return 2
else:
    return 3
Tim Pietzcker
As @Jesse Dillon noted, this only works with strings, and would also return 1 if `s=='ab'`, but the constraints of the question were specific about this - `s` is a one-letter string.
Tim Pietzcker
Right, I wouldn't say that this is wrong given the parameters of the question. +1 for demonstrating that `__contains__` is a predicate for testing the existence of substrings within strings.
Jesse Dhillon
+10  A: 
 d = {'a':1, 'b':1, 'c':2, 'd':2}
 return d.get(s, 3)
James Roth
I'd say this is fairly clever.
Jesse Dhillon
Now I am curious if this is faster.
pythonrubies
It could even be reduced further: `return {'a':1, 'b':1, 'c':2, 'd':2}.get(s, 3)`
James Roth
+1  A: 

Maybe little more self documenting using if else:

d = {'a':1, 'b':1, 'c':2, 'd':2} ## good choice is to replace case with dict when possible
return d[s] if s in d else 3

Also it is possible to implement the popular first answer with if else:

  return (1 if s in ('a', 'b') else (2 if s in ('c','d') else 3))
Tony Veijalainen
+1  A: 
return 1 if (x in 'ab') else 2 if (x in 'cd') else 3
Robert William Hanks
this would allow `x` to be `'ab'` or `'cd'`
SilentGhost
@SilentGhost: the intent is to solve the proposed problem not a variant, OP said "I have a variable s which contains a one letter string"
Robert William Hanks