6

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:

  1. Is there a built-in function which acts like a or b?
  2. Is there a built-in implementation of such a null coalesce function?

The answer to both seems to be no, but that would be somewhat surprising to me.

2
  • 3
    None-coalescing, -aware, -safe operators is proposed on PEP 505 which has deferred status. Commented Aug 16, 2019 at 1:13
  • 2
    @alkasm nope, that's the bitwise OR operator. Commented Aug 16, 2019 at 1:18

3 Answers 3

9

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)
Sign up to request clarification or add additional context in comments.

3 Comments

null_coalesce([0,0,3]) == [0, 0, 3] What's going on there?
@wjandrea it's star-args, not a single arg. You should be passing 0, 0, 3 to the function, not [0, 0, 3] if you want to just return 3.
@alkasm ohhh, oops
6

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 and and 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 call some_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)

4 Comments

Alternatively, operator.or_
@alkasm operator.__or__ and operator.or_ are for bitwise OR. That's another thing. I quickly edited my answer after noticing.
The ability to have a predicate in this is really nice +1
Oops! Good call!
1

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.