Best way to do conditional assignment in python - 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] => ()
...

Related

One liner to "assign if not None"

Is there a way to do an assignment only if the assigned value is not None, and otherwise do nothing?
Of course we can do:
x = get_value() if get_value() is not None
but this will read the value twice. We can cache it to a local variable:
v = get_value()
x = v if v is not None
but now we have made two statements for a simple thing.
We could write a function:
def return_if_not_none(v, default):
if v is not None:
return v
else:
return default
And then do x = return_if_not_none(get_value(), x). But surely there is already a Python idiom to accomplish this, without accessing x or get_value() twice and without creating variables?
Put in another way, let's say =?? is a Python operator similar to the C# null coalesce operator. Unlike the C# ??=, our fictional operator checks if the right hand side is None:
x = 1
y = 2
z = None
x =?? y
print(x) # Prints "2"
x =?? z
print(x) # Still prints "2"
Such a =?? operator would do exactly what my question is asking.
In python 3.8 you can do something like this
if (v := get_value()) is not None:
x = v
Updated based on Ryan Haining solution, see in comments

Can I remove double evaluation whilst keeping lambda expression

def bar(x):
# some expensive calculation
<snip>
foo = lambda(x): bar(x) if bar(x) > 10 else 0
However here I have calculated foo twice. Is there a way to still write this as a one liner but avoid the double evaluation. I tried
foo = lambda(x): v if (v = bar(x)) > 10 else 0
but that doesn't work.
Preevaluate:
foo = lambda(x): x if x > 10 else 0
result = foo(bar(x))
Here you can see what is the similarities to your code:
lambda (x): foo(x) if foo(x) > 10 else 0 == (lambda(x): x if x > 10 else 0)(foo(x))
What you seek is not possible unless you create some kind of mutable state object or some other weird trick.
#Alakazam answer is tricky and smart, use it on your own risk. It can be better using iterators and next to avoid extra intermediate list:
lambda x: next(res if res > 10 else 0 for res in (bar(x), ))
Here you have the live example
Yes, you can avoid double evaluation in a lambda. But it's really ugly, and you should avoid it. Here's how it looks:
foo = lambda x: next(b if b > 10 else 0 for b in [bar(x)])
Is this easy to read and understand? No, absolutely not. I'm not going to explain it; see if you can figure it out. This clearly isn't a good solution, so what should you do instead? You should use a real function instead of a lambda.
def foo(x):
b = bar(x)
return b if b > 10 else 0
This is much easier to read and clearly better.
You may want to do this only if your function is very long to execute, else you shouldn't mind running it twice. You can't use list comprehension.
foo = lambda x: [res if res>10 else 0 for res in [bar(x)]][0]
Might as well just define another function:
def bar(x):
# some expensive calculation
<snip>
def bar_threshold(x,t):
y = bar(x)
return y if y>t else 0
foo = lambda x: bar_threshold(x,10)
(or redefine bar, if you find yourself using it only with the threshold)
I've tried by make some function decorator / wrapper...
def funcdec(func):
def inner(x):
if func(x) > 10:
return func(x)
else:
return 0
then
#funcdec
def bar(x):
return x * 2
then i try....:
foo = lambda x: bar(x)
give me result :
foo(2)
return 0
and
foo(10)
return 20

What is the best way to return items in Python that meet a specific condition

The code I'm currently using is:
def f(*args):
lst=[str(i) for i in args]
if len(lst)==1:lst = lst[0]
return lst
What I would like is:
a=f(1) #'1', not [1]
a,b = f(1,2) #'1', '2'
Only one argument would be a list, which would be represented by a.
What alternative exists aside from using an if statement?
Yes:
return lst[0] if len(lst) == 1 else lst
Returning different types like that can be confusing. I'd recommend using
a = f(1)[0]
or
[a] = f(1)
or
a, = f(1)
If I understand you correctly, no. If you accept variable arguments with *args, then you get a list, even if there is only one argument.
You could of course separate the first argument with def f(first, *rest), but then you have to do special-casing to combine the elements when you do want a list.
I suggest to use yield:
def f(*args):
for i in args:
yield str(i)
a, = f(1)
print a
a, b = f(1, 2)
print a, b
which returns:
1
1 2
is it what you want?

Pythonic way to select first variable that evaluates to True

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

how to program functions with alternative return value signatures in python? (next() for alternative iterators)

e.g. so that these would both work - is it possible?
(val,VAL2) = func(args)
val = func(args)
Where val is not a tuple
For example I'd like these to work for my custom object something
for item in something:
do_item(item) #where again item - is not a tuple
for (item,key) in something:
do_more(key,item)
I thought that I need to implement next() function in two different ways...
edit: as follows from the answers below, this should not really be done.
If you mean, can the function act differently based on the return types the caller is expecting, the answer is no (bar seriously nasty bytecode inspection). In this case, you should provide two different iterators on your object, and write something like:
for item in something: # Default iterator: returns non-tuple objects
do_something(item)
for (item,key) in something.iter_pairs(): # iter_pairs returns different iterator
do_something_else(item, key)
eg. see the dictionary object, which uses this pattern. for key in mydict iterates over the dictionary keys. for k,v in mydict.iteritems() iterates over (key, value) pairs.
[Edit] Just in case anyone wants to see what I mean by "seriously nasty bytecode inspection", here's a quick implementation:
import inspect, opcode
def num_expected_results():
"""Return the number of items the caller is expecting in a tuple.
Returns None if a single value is expected, rather than a tuple.
"""
f = inspect.currentframe(2)
code = map(ord, f.f_code.co_code)
pos = f.f_lasti
if code[pos] == opcode.opmap['GET_ITER']: pos += 1 # Skip this and the FOR_ITER
if code[pos] > opcode.EXTENDED_ARG: pos +=5
elif code[pos] > opcode.HAVE_ARGUMENT: pos +=3
else: pos += 1
if code[pos] == opcode.opmap['UNPACK_SEQUENCE']:
return code[pos+1] + (code[pos+2] << 8)
return None
Usable something like:
class MagicDict(dict):
def __iter__(self):
if num_expected_results() == 2:
for k,v in self.iteritems():
yield k,v
else:
for k in self.iterkeys():
yield k
d=MagicDict(foo=1, bar=2)
print "Keys:"
for key in d:
print " ", key
print "Values"
for k,v in d:
print " ",k,v
Disclaimer: This is incredibly hacky, insanely bad practice, and will cause other programmers to hunt you down and kill you if they ever see it in real code. Only works on cpython (if that). Never use this in production code (or for that matter, probably any code).
Have you tried that? It works.
def myfunction(data):
datalen = len(data)
result1 = data[:datalen/2]
result2 = data[datalen/2:]
return result1, result2
a, b = myfunction('stuff')
print a
print b
c = myfunction('other stuff')
print c
In fact there is no such thing as "return signature". All functions return a single object. It seems that you are returning more than one, but in fact you wrap them into a container tuple object.
Yes it's doable:
def a(b):
if b < 5:
return ("o", "k")
else:
return "ko"
and the result:
>>> b = a(4)
>>> b
('o', 'k')
>>> b = a(6)
>>> b
'ko'
I think the thing after is to be careful when you will use the values returned...
>>> def func(a,b):
return (a,b)
>>> x = func(1,2)
>>> x
(1, 2)
>>> (y,z) = func(1,2)
>>> y
1
>>> z
2
That doesn't really answer your question. The real answer is that the left side of the assignment doesn't affect the returned type of the function and can't be used to distinguish between functions with different return types. As noted in other answers, the function can return different types from different return statements but it doesn't know what's on the other side of the equals sign.
In the case of this function, it returns a tuple. If you assign it to x, x has the value of the tuple. (y, z) on the left side of the assignment is "tuple unpacking". The tuple returned by func() is unpacked into y and z.
Update:
Given the example use case, I'd write different generators to handle the cases:
class Something(object):
def __init__(self):
self.d = {'a' : 1,
'b' : 2,
'c' : 3}
def items(self):
for i in self.d.values():
yield i
def items_keys(self):
for k,i in self.d.items():
yield i,k
something = Something()
for item in something.items():
....: print item
....:
1
3
2
for item,key in something.items_keys():
....: print key, " : ", item
....:
a : 1
b : 2
c : 3
Or
You can return a tuple:
In [1]: def func(n):
...: return (n, n+1)
...:
In [2]: a,b = func(1)
In [3]: a
Out[3]: 1
In [4]: b
Out[4]: 2
In [5]: x = func(1)
In [6]: x
Out[6]: (1, 2)
Yes, both would work. In the first example, val1 and val2 would have the two values. In the second example, val would have a tuple. You can try this in your python interpreter:
>>> def foo():
... return ( 1, 2 )
...
>>> x = foo()
>>> (y,z) = foo()
>>> x
(1, 2)
>>> y
1
>>> z
2
It's possible only if you're happy for val to be a 2-item tuple (or if args need not be the same in the two cases). The former is what would happen if the function just ended with something like return 23, 45. Here's an example of the latter idea:
def weirdfunc(how_many_returns):
assert 1 <= how_many_returns <= 4
return 'fee fie foo fum'.split()[:how_many_returns]
var1, var2 = weirdfunc(2) # var1 gets 'fee', var2 gets 'fie'
var, = weirdfunc(1) # var gets 'fee'
This is asking for major confusion. Instead you can follow dict with separate keys, values, items, etc. methods, or you can use a convention of naming unused variables with a single underscore. Examples:
for k in mydict.keys(): pass
for k, v in mydict.items(): pass
for a, b in myobj.foo(): pass
for a, _ in myobj.foo(): pass
for _, b in myobj.foo(): pass
for _, _, _, d in [("even", "multiple", "underscores", "works")]:
print(d)
for item in something: # or something.keys(), etc.
do_item(item)
for item, key in something.items():
do_more(key, item)
If this doesn't fit your function, you should refactor it as two or more functions, because it's clearly trying to fulfill two or more different goals.

Categories

Resources