Pythonic way to select first variable that evaluates to True - python

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?

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

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

So long as default evaluates to True:
result = next((x for x in (a, b, c, d , e, default) if x))

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])

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

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.

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"!-)

Related

Best way to simplify multiple conditional statements

I have a single action to perform depending on the values of a,b,c,d but I may not get all the 4 values every time there will be 16 possible permutations I can get ϕ ,{a},{b},{c},{d},{a,b},{a,c},{a,d},{b,c},{b,d},{c,d},{a,b,c},{a,b,d},{a,c,d},{b,c,d},{a,b,c,d}.
def call_me (a=None,b=None,c=None,d=None):
if a:
a1 = a+1
if b:
b1 = b+2
if c:
c1 = c+3
if d:
d1 = d+4
if (a<a1) and (b<b1) and (c<c1) and (d<d1):
#Do something
return "something"
If I call call_me(a = 1,b = 2,c = 3,d =4) the program will work but in case If I do call_me(a = 1,b = 2,c = 3) it will throw me an error UnboundLocalError: local variable 'd1' referenced before assignment
So the only way I can think is to cover all the combinations (2 ^ N)
if a and b and c and d:
if (a<a1) and (b<b1) and (c<c1) and (d<d1):
return "something 1"
if a and b and c:
if (a<a1) and (b<b1) and (c<c1):
return "something 2"
if a and b and d:
if (a<a1) and (b<b1) and (d<d1):
return "something 3"
#and so on...
Is there any way to simplify this by not using so many if statements?
You can check for existence inside of each conditional where you're using the value:
if (a is None or a<a1) and (b is None or b<b1) and (c is None or c<c1) and (d is None or d<d1):
# Do something
This way if any of the passed values is still None, that parenthesized expression will become True and won't interfere with the rest of the checks.
Another way to do it is to check the condition related to each variable at the same time as you check for existence, and just early-exit if it fails:
if a:
a1 = a+1
if not a<a1:
return
if b:
# etc.
# All conditions passed, do something.

python convert a string to a logic gate function

I have a python string like: "(a and b and c) or (d and e)", where a, b, c, d and e are conditions of some kind. As you can probably see, this is actually a logical expression.
I'd like to somehow convert it into a logic-gate-like function, say, f. So I would pass a number of true conditions to f, and get the logical results. Examples:
If I pass it (d and e) (i.e., d and c are true), f returns True.
If I pass it (a and b and c), f returns True.
If I pass it just a, f returns False.
If I pass it just b and c, f returns False.
I have no idea how to tackle such a question. strtobool doesn't quite handle my requirements, and I'm not sure how to even convert the given string into a function. As for passing it true conditions as input, I'm thinking of passing it a list of True booleans, i.e. all conditions are False by default. e.g. from the first example above:
d = True; e = True
f([d, e])
> True
d = True; e = False
f([d])
> False
You're looking for eval.
Essentially, you can do-
eval('d and e', {'d': True, 'e': False})
Which passes the d and e string to evaluate, and also passes in some globals, to set the value of those variables.
It's often better to use the third argument, locals, instead of globals however.
eval('d and e', {}, {'d': True, 'e': False})
This will achieve the same thing, except d and e are set in local scope, the second argument is just empty.

Declare multiple variable in a single line in python

I've code,
var a,b,c,d;
I need to rewrite this in python.
I'm not sure, since I'm new to python, how to define multiple variables in a sinlge line with no assignment.
I thought of doing
> a=None
> b=None
> c=None
> d=None
But it should be in one line
More pythonic way is tuple unpacking:
a, b, c, d = 1, 2, 3, 4
Or if you want to initialize to single value
a = b = c = d = 1
You could also use semi-colon (although not encouraged)
a=1; b=2; c=3; d=4
All of them would work.
You could use tuple unpacking:
a, b, c = 1, 2, 3
But to be honest, it would more Pythonic to do the assignments on separate lines.
The "to a single value" part warrants a little bit of extra explanation.
a = b = c = d = None
a = 1
print(b)
>>> None
The above statement does set the value of each variable to None, but with some other types of values, this may not always be the case. Python is an object-oriented language and if you replace the value None with another type of value that is a Python object you might get results that you don't expect (because Python sets all four variables to literally the same object). Consider:
a = b = c = d = list()
a.append(1)
print(b)
>>>[1]
The reason that the result is different is because a, b, c, and d are all referring to the same list. This is a fundamental concept in Python and a really important one to remember, and shows why making these types of one-line declarations can be potentially problematic.
As others have said, declaring your variables on the go (as you need them) is probably the better way to go overall, as it helps to avoid these types of "less obvious" declaration issues.
This will do it in a single line and several expressions
a=None; b=None; c=None; d=None
This will do it in a single line declaring all vars at once with the same value
a = b = c = d = None
And as pointed out in comments, you could even do it with 0 line since you can just use your vars on the go without prior declaration.

Changing a local variable in a function from another function

First, here's my example code:
EDIT: I should have specified, in my real code, that_func() is already returning another value, so I want it to return one value, and change c in addition
EDIT 2: Code edited to show what I mean
def this_func():
c=1 # I want to change this c
d=that_func()
print(c, d)
def that_func():
this_func.c=2 #Into this c, from this function
return(1000) #that_func should also return a value
this_func()
What I want to do is change the local variable c in this_func() to the value I assign it in that_func(), so that it prints 2 instead of 1.
From what I've gathered online, this_func.c=2 should do just that, but it doesn't work. Am I doing something wrong, or have I misunderstood?
Thanks for any and all help.
Yes, you misunderstood.
functions are not class. You can't access variables of a function like that.
Obviously, it's not the smartest of code that can be written, but this code should give an idea about how to use variables of a function.
def this_func():
c=1 # I want to change this c
c=that_func(c) # pass c as parameter and receive return value in c later
print(c)
def that_func(b): # receiving value of c from this_func()
b=2 # manipulating the value
return b #returning back to this_func()
this_func()
Wrap it in an object and pass it to that_func:
def this_func():
vars = {'c': 1}
d = that_func(vars)
print vars['c'], d
def that_func(vars):
vars['c'] = 2
return 1000
Alternatively, you can pass it in as a regular variable and that_func can return multiple values:
def this_func():
c = 1
c, d = that_func(c)
print c, d
def that_func(c):
c = 2
return c, 1000

Best way to do conditional assignment in python

I tend to use this a lot, but it's ugly:
a = (lambda x: x if x else y)(get_something())
So I wrote this function:
def either(val, alt):
if val:
return val
else:
return alt
So you can do:
a = either(get_something(), y)
Is there a built-in function for this (similar to ISNULL in T-SQL)?
The or operator does what you want:
get_something() or y
In fact, it's chainable, like COALESCE (and unlike ISNULL). The following expression evaluates to the left-most argument that converts to True.
A or B or C
Easy!
For more conditional code:
a = b if b else val
For your code:
a = get_something() if get_something() else val
With that you can do complex conditions like this:
a = get_something() if get_something()/2!=0 else val
You may use:
a = get_something() or y
If get_something is True in boolean context, its value will be assigned to a. Otherwise - y will be assigned to a.
You can use a simple or, like so:
>>> a = None
>>> b = 1
>>> c = (a or b) # parentheses are optional
>>> c
1
I have provided an answer to this question to another user. Check it out here:
Answer to similar question
To respond quickly here, do:
x = true_value if condition else false_value
I'm also using the (a,b)[condition based on the value of a] form, saving the result of the get_something() call into a, in the rare cases that are best presented here: http://mail.python.org/pipermail/python-list/2002-September/785515.html
...
a=0 b=None a or b => None (a,b)[a is None] => 0
a=() b=None a or b => None (a,b)[a is None] => ()
...

Categories

Resources