Search code examples
pythonpython-3.xwalrus-operator

Two Walrus Operators in one If Statement


Is there a correct way to have two walrus operators in 1 if statement?

if (three:= i%3==0) and (five:= i%5 ==0):
    arr.append("FizzBuzz")
elif three:
    arr.append("Fizz")
elif five:
    arr.append("Buzz")
else:
    arr.append(str(i-1))

This example works for three but five will be "not defined".


Solution

  • The logical operator and evaluates its second operand only conditionally. There is no correct way to have a conditional assignment that is unconditionally needed.

    Instead use the "binary" operator &, which evaluates its second operand unconditionally.

    arr = []
    for i in range(1, 25):
        #                        v force evaluation of both operands
        if (three := i % 3 == 0) & (five := i % 5 == 0):
            arr.append("FizzBuzz")
        elif three:
            arr.append("Fizz")
        elif five:
            arr.append("Buzz")
        else:
            arr.append(str(i))
    
    print(arr)
    # ['1', '2', 'Fizz', '4', 'Buzz', 'Fizz', '7', '8', 'Fizz', 'Buzz', '11', ...]
    

    Correspondingly, one can use | as an unconditional variant of or. In addition, the "xor" operator ^ has no equivalent with conditional evaluation at all.

    Notably, the binary operators evaluate booleans as purely boolean - for example, False | True is True not 1 – but may work differently for other types. To evaluate arbitrary values such as lists in a boolean context with binary operators, convert them to bool after assignment:

    #  |~~~ force list to boolean ~~| | force evaluation of both operands
    #  v    v~ walrus-assign list ~vv v
    if bool(lines := list(some_file)) & ((today := datetime.today()) == 0):
       ...
    

    Since assignment expressions require parentheses for proper precedence, the common problem of different precedence between logical (and, or) and binary (&, |, ^) operators is irrelevant here.