Python has a great syntax for null coalescing:
c = a or b
This sets c
to a
if a
is not False
, None
, empty, or 0
, otherwise c
is set to b
.
(Yes, technically this is not null coalescing, it's more like bool
coalescing, but it's close enough for the purpose of this question.)
There is not an obvious way to do this for a collection of objects, so I wrote a function to do this:
from functools import reduce
def or_func(x, y):
return x or y
def null_coalesce(*a):
return reduce(or_func, a)
This works, but writing my own or_func
seems suboptimal - surely there is a built-in like __or__
? I've attempted to use object.__or__
and operator.__or__
, but the first gives an AttributeError
and the second refers to the bitwise |
(or) operator.
As a result I have two questions:
a or b
?The answer to both seems to be no, but that would be somewhat surprising to me.
It's not exactly a single built-in, but what you want to achieve can be easily done with:
def null_coalesce(*a):
return next(x for x in a if x)
It's lazy, so it does short-circuit like a or b or c
, but unlike reduce
.
You can also make it null-specific with:
def null_coalesce(*a):
return next(x for x in a if x is not None)
Is there a built-in function which I can use which acts like a or b?
No. Quoting from this answer on why:
The
or
andand
operators can't be expressed as functions because of their short-circuiting behavior:False and some_function() True or some_function()
in these cases,
some_function()
is never called.A hypothetical
or_(True, some_function())
, on the other hand, would have to callsome_function()
, because function arguments are always evaluated before the function is called.
Is there a built-in implementation of such a null coalesce function?
No, there isn't. However, the Python documentation page for itertools
suggests the following:
def first_true(iterable, default=False, pred=None):
"""Returns the first true value in the iterable.
If no true value is found, returns *default*
If *pred* is not None, returns the first item
for which pred(item) is true.
"""
# first_true([a,b,c], x) --> a or b or c or x
# first_true([a,b], x, f) --> a if f(a) else b if f(b) else x
return next(filter(pred, iterable), default)
Marco has it right, there's no built-in, and itertools has a recipe. You can also pip install boltons
to use the boltons.iterutils.first()
utility, which is perfect if you want short-circuiting.
from boltons.iterutils import first
c = first([a, b])
There are a few other related and handy reduction tools in iterutils
, too, like one()
.
I've done enough of the above that I actually ended up wanting a higher-level tool that could capture the entire interaction (including the a
and b
references) in a Python data structure, yielding glom
and its Coalesce
functionality.
from glom import glom, Coalesce
target = {'b': 1}
spec = Coalesce('a', 'b')
c = glom(target, spec)
# c = 1
(Full disclosure, as hinted above, I maintain glom
and boltons
, which is good news, because you can bug me if you find bugs.)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With