Search code examples
pythonsyntaxternary-operatoriterable-unpacking

Python syntax: unpacking multiple variables from function return value in ternary expression gives unexpected result


I have noticed an odd problem with unpacking multiple values in a ternary expression. Firstly, a MWE illustrating the syntax, in which the intent is to unpack the tuple on the right and assign the list inside it to the first name on the left, and the number to the second name.

condition = False
a, b = [1, 2], 3 if not condition else None, None  # ValueError: too many values to unpack
def foo():
    return [1, 2], 3
([1, 2], 3) == foo()  # True
a, b = foo()  # works as expected: a = [1, 2] and b = 3
a, b = foo() if not condition else None, None  # now a = ([1, 2], 3) and b is None

My question is to understand the rationale behind the syntax here. If condition evaluates as false, why would the last part of the ternary expression, the else clause, ever be evaluated at all? Is Python assigning the second None to b? This makes no sense to me, but I cannot see how else a) no ValueError is raised to tell me that unpacking has not worked at all (if my syntax somehow compels Python to treat the whole tuple as a single entity rather than unpacking it) and b) a value is nonetheless assigned to b. The obvious next tests:

a, b = foo() if not condition else None, 'test'  # Now a = ([1, 2], 3) and b = 'test'
a, b = (lambda x: [[1, 2], 3])('blah') if not condition else None, 'test'  # Same result with a lambda function.

So it seems that the else clause is being evaluated. Why does this happen? Is there an elegant way of rewriting this to allow me to call the function inside such a ternary expression, aside from the obvious and arguably clumsier

if not condition:
    a, b = foo()
else:
    a, b = None, None

Solution

  • This is just precedence. Python is parsing this as:

    a, b = (foo() if not condition else None), None
    

    For your expected result, you'll need to add parentheses around the Nones:

    a, b = foo() if not condition else (None, None)