Compressing `x if x else y` statement in Python

Rubens picture Rubens · Dec 31, 2012 · Viewed 70.7k times · Source

I'm quite acquainted with Python's ternary operator approach:

value = foo if something else bar

My question is very simple: without prior assignments, is there anyway to reference the term being evaluated in (if ...) from one of the return operands (... if or else ...)?

The motivation here is that sometimes I use expressions in if ... that are exactly what I'd like to have as result in the ternary operation; happens though that, for small expressions, there's no problem repeating it, but for a bit longer expressions, it goes somewhat nasty. Take this as an example:

value = info.findNext("b") if info.findNext("b") else "Oompa Loompa"

Answer

abarnert picture abarnert · Dec 31, 2012

There is no way to do this, and that's intentional. The ternary if is only supposed to be used for trivial cases.

If you want to use the result of a computation twice, put it in a temporary variable:

value = info.findNext("b")
value = value if value else "Oompa Loompa"

Once you do this, it becomes clear that you're doing something silly, and in fact the pythonic way to write this is:

value = info.findNext("b")
if not value:
    value = "Oompa Loompa"

And that's actually 5 fewer keystrokes than your original attempt.

If you really want to save keystrokes, you can instead do this:

value = info.findNext("b") or "Oompa Loompa"

But that's discouraged by many style guides, and by the BDFL.

If you're only doing this once, it's better to be more explicit. If you're doing it half a dozen times, it's trivial—and much better—to make findNext take an optional default to return instead of None, just like all those built-in and stdlib functions:

def findNext(self, needle, defvalue=None):
    # same code as before, but instead of return None or falling off the end,
    # just return defvalue.

Then you can do this:

value = info.findNext("b", "Oompa Loompa")