Question

Situation

(Note: The following situation is just exemplary. This question applys to anything that can evaluate to bool)

A default list should be used if the user does not provide a custom list:

default_list = ...
custom_list = ...
if custom_list:
    list = custom_list
else:
    list = default_list

You can shorten this to:

default_list = ...
custom_list = ...
list = custom_list if custom_list else default_list

Now, as per https://docs.python.org/2.7/reference/expressions.html#or ...

The expression x or y first evaluates x; if x is true, its value is returned; otherwise, y is evaluated and the resulting value is returned.

..., or does not return a boolean, but rather the first value whose boolean conversion is not false. Therefore, the following would be valid code:

list = custom_list or default_list

This is similar to the C# Null Coalescing Operator, except it should be recoined in Python as False Coalescing Operator, which returns the first non-false argument.

Question

The last example seems to be easier to read, but is it considered pythonic?

Neither pep8 (the program) nor pylint do complain.

Was it helpful?

Solution

That is perfectly valid and you can use that. Even the documentation of or has an example for the same.

Note that neither and nor or restrict the value and type they return to False and True, but rather return the last evaluated argument. This is sometimes useful, e.g., if s is a string that should be replaced by a default value if it is empty, the expression s or 'foo' yields the desired value.

However, the or method has a limitation. If you want to purposefully allow a Non-Truthy value, then it is not possible with that.

Let us say you want to allow an empty list

my_list = [] or default_list

will always give default_list. For example,

print [] or [1, 2, 3]
# [1, 2, 3]

But with conditional expression we can handle it like this

custom_list if isinstance(custom_list, list) else default_list

Dusting off the older documents, quoting the BDFL's FAQ,

4.16. Q. Is there an equivalent of C's "?:" ternary operator?

A. Not directly. In many cases you can mimic a?b:c with a and b or c, but there's a flaw: if b is zero (or empty, or None -- anything that tests false) then c will be selected instead. In many cases you can prove by looking at the code that this can't happen (e.g. because b is a constant or has a type that can never be false), but in general this can be a problem.

Steve Majewski (or was it Tim Peters?) suggested the following solution: (a and [b] or [c])[0]. Because [b] is a singleton list it is never false, so the wrong path is never taken; then applying [0] to the whole thing gets the b or c that you really wanted. Ugly, but it gets you there in the rare cases where it is really inconvenient to rewrite your code using 'if'.

OTHER TIPS

Yes, using or for it's short-circuiting properties was the norm, before the conditional expression construct was added to the language.

It was, and still is, perfectly pythonic to use:

foo = bar or baz

to fall back to a default if bar is false-y (evaluates to false in a boolean context). The fact that it short-circuits also lets you do things like:

foo = bar or expensive_calculation(baz)  # only if bar is false-y

and not have expensive_calculation() execute if bar is truth-y (evaluates to true in a boolean context). Similarly, you can use and to ensure that preconditions are met:

foo = bar and bar(baz)  # call `bar()` only if it is truth-y

You should use a conditional expression for:

foo = bar and spam or eggs

however. That's what the conditional expression aims to replace. The idea was that if bar is truth-y, then spam is picked, else eggs is picked. But that breaks down if spam is false-y! This was a common source of errors, while

foo = spam if bar else eggs

always picks spam if bar is truth-y.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top