pylang
pylang

Reputation: 44465

How to write one-line ternary expressions using `is None` in Python?

I am interested in using one-line ternary expressions instead of traditional multiline if-else blocks. However, expressions comparing is None give different results.

Here is an example comparing multiline and one-line variants of two forms of a conditional expression for a predicate:

  1. if pred is not None
  2. if pred is None

Code

import operator as op


# First form
def is_not_none_multiline(a, b, pred=None):
    if pred is not None:
        predicate = pred
    else:
        predicate = lambda x, y: x + y
    return predicate(a, b)

def is_not_none_oneline(a, b, pred=None):
    predicate = pred if pred is not None else lambda x, y: x + y
    return predicate(a, b)

# Second Form
def is_none_multiline(a, b, pred=None):
    if pred is None:
        predicate = lambda x, y: x + y
    else:
        predicate = pred
    return predicate(a, b)

def is_none_oneline(a, b, pred=None):
    predicate = lambda x, y: x + y if pred is None else pred
    return predicate(a, b)

Tests

Here are tests for optional arguments in mutliline and one-line variants. The final result was unexpected:

assert is_not_none_multiline(1, 2) == 3
assert is_not_none_oneline(1, 2) == 3
assert is_not_none_multiline(1, 2, pred=op.mul) == 2
assert is_not_none_oneline(1, 2, pred=op.mul) == 2

assert is_none_multiline(1, 2) == 3
assert is_none_oneline(1, 2) == 3
assert is_none_multiline(1, 2, pred=op.mul) == 2
assert is_none_oneline(1, 2, pred=op.mul) == 2

# ----> 4 assert is_none_oneline(1, 2, pred=op.mul) == 2
# AssertionError: 

Although pred is not None works as one-line:

predicate = pred if pred is not None else lambda x, y: x + y

pred is None does not work as one-line:

predicate = lambda x, y: x + y if pred is None else pred

Details

Apparantly, the pred function is not evaluated when passed in as a keyword to is_none_oneline(). Rather, it is returned:

print(is_none_oneline(1, 2, pred=op.mul))
# <built-in function mul>

This analysis was verified in a Python Tutor visualization when executing both variants of the second form (see visualizations here is not None multiline, is not None one-line, is None multiline, is None one-line).

Questions

It is unclear why an equivalent ternary expression returns a function rather than a computed value.

  1. Can someone explain why the predicate is not evaluated in the second form - a one-line, pred is None expression?
  2. How does one correctly write pred is None in one-line?

Upvotes: 2

Views: 844

Answers (1)

wim
wim

Reputation: 362557

Just a simple case of operator precedence.

You're getting a callable which returns a callable. I think you wanted this instead, to make sure the conditional gets grouped the other way:

def is_none_oneline(a, b, pred=None):
    predicate = (lambda x, y: x + y) if pred is None else pred
    return predicate(a, b)

Upvotes: 3

Related Questions