views:

271

answers:

7

I have some variables and I want to select the first one that evaluates to True, or else return a default value.

For instance I have a, b, and c. My existing code:

result = a if a else (b if b else (c if c else default))

Another approach I was considering:

result = ([v for v in (a, b, c) if v] + [default])[0]

But they both feel messy, so is there a more Pythonic way?

+10  A: 

Did you mean returning first value for what bool(value)==True? Then you can just rely on the fact that boolean operators return last evaluated argument:

result = a or b or c or default
Denis Otkidach
N.B (to the OP) the empty string (`""`) has a false boolean value (`bool("") == False`); keep that in mind if you don't consider it "false" in your program. Same applies to `()` (empty tuple), `{}` (empty dict), `[]` (empty list) and zero (in any numeric type).
ΤΖΩΤΖΙΟΥ
+17  A: 

If one variable is not "defined", you can't access its name. So any reference to 'a' raises a NameError Exception.

In the other hand, if you have something like:

a = None
b = None
c = 3

you can do

default = 1
r = a or b or c or default
# r value is 3
Juanjo Conti
Wouldn't either of the two options the asker presented run into the same NameErrorException?
donut
That's right. You can't read a variable before assigning something to it.
Juanjo Conti
+1  A: 

Don't know if this works in every case, but this works for this case.

a = False
b = "b"
c = False
default = "default"
print a or b or c or default # b
Josh Smeaton
+1  A: 

How about this ?

a=None  
b=None  
c=None  
val= reduce(lambda x,y:x or y,(a,b,c,"default"))  
print val

The above prints "default". If any of the inputs is defined, val would contain the first defined input.

sateesh
+1  A: 

So long as default evaluates to True:

result = next((x for x in (a, b, c, d , e, default) if x))
Robert Rossney
If you've decided to use `next`, you might as well use its `default` argument
abyx
+2  A: 

You could do something like this (in contrast to the other answers this is a solution where you don't have to define the 'missing' values as being either None or False):

b = 6
c = 8

def first_defined(items):
    for x in items:
        try:
            return globals()[x]
            break
        except KeyError:
            continue

print first_defined(["a", "b", "c"])

In order to avoid NameErrors when a, b or c isn't defined: give the function a list of strings instead of variable references (you can't pass non-existing references). If you are using variables outside the 'globals()' scope, you could use getattr with its default argument.

--

If a, b and c are defined, I'd go for something like this (considering the fact that an empty string, None or False evaluate to a boolean False):

a = None
b = 6
c = 8

def firstitem(items):
    for x in items:
        if x:
            return x
            break
        else:
            continue

print firstitem([a, b, c])
ChristopheD
You don't need to `break` after a return: you're already out of the loop. You also don't need to `else: continue` at the end of a loop: that's what would happen anyhow.
Mike Graham
+1  A: 

If by defined you mean ever assigned any value whatsoever to in any scope accessible from here, then trying to access an "undefined" variable will raise a NameError exception (or some subclass thereof, but catching NameError will catch the subclass too). So, the simplest way to perform, literally, the absolutely weird task you ask about, is:

for varname in ('a', 'b', 'c'):
  try: return eval(varname)
  except NameError: pass
return default

Any alleged solution lacking a try/except won't work under the above meaning for "defined". Approaches based on exploring specific scopes will either miss other scopes, or be quite complex by trying to replicate the scope-ordering logic that eval does for you so simply.

If by "defined" you actually mean "assigned a value that evaluates to true (as opposed to false)", i.e., all values are actually defined (but might happen to be false, and you want the first true value instead), then the already-proposed a or b or c or default becomes the simplest approach. But that's a totally different (and even weirder!) meaning for the word "defined"!-)

Alex Martelli