16

In Python (3.11) why does the use of an assignment expression (the "walrus operator") require wrapping in parentheses when used inside an f-string?

For example:

#!/usr/bin/env python

from pathlib import Path

import torch


DEVICE = torch.device("cuda" if torch.cuda.is_available() else "cpu")

ckpt_dir = Path("/home/path/to/checkpoints")

_ckpt = next(ckpt_dir.iterdir())
print(_ckpt)
sdict = torch.load(_ckpt, map_location=DEVICE)

model_dict = sdict["state_dict"]

for k, v in model_dict.items():
    print(k)
    print(type(v))
    print(_shape := v.size())
    print(f"{(_numel := v.numel())}")
    print(_numel == torch.prod(torch.tensor(_shape)))

The code block above with print(f"{_numel := v.numel()}") instead does not parse.

What about the parsing / AST creation mandates this?

3
  • 7
    It's most likely expecting just a variable or, inside (...), a more complete expression. For what it's worth, assigning a variable inside an f-string is pretty dirty and should be avoided.
    – tadman
    Commented Aug 14, 2023 at 18:26
  • 21
    Hint: can you think of another reason why : could be used inside {} in an f-string? (Something innate to the formatting system, not to do with the expression.) Commented Aug 14, 2023 at 18:28
  • 2
    Any particular reason you are attempting to perform variable assignment within an f-string? Assigning data and formatting data are very different operations and should generally be performed independently.
    – h0r53
    Commented Aug 14, 2023 at 18:39

2 Answers 2

23

This behavior was explicitly specified in the original PEP for the assignment expressions (aka the walrus operator).

The reason for this was to preserve backward compatibility with formatted string literals. Before assignment expressions were added, you could already write f-strings like f"{x:=y}", which meant "format x using the format specification =y".

Quoting PEP 572 – Assignment Expressions:

Assignment expressions inside of f-strings require parentheses. Example:

>>> f'{(x:=10)}'  # Valid, uses assignment expression
'10'
>>> x = 10
>>> f'{x:=10}'    # Valid, passes '=10' to formatter
'        10'

This shows that what looks like an assignment operator in an f-string is not always an assignment operator. The f-string parser uses : to indicate formatting options. To preserve backward compatibility, assignment operator usage inside of f-strings must be parenthesized. As noted above, this usage of the assignment operator is not recommended.

0
7

Both the colon : and the = have meanings inside f-strings, and the walrus := does the same as just a colon :.

x = 4
print(f"{x=}")  # prints "x = 4"
print(f"{x:10}")  # prints output in 10 spaces
print(f"{x:=10}")  # does same as :

The disassembly of {x:=10} just loads =10 and passes it to the colon format operation.

  5           4 LOAD_GLOBAL              0 (print)
              6 LOAD_FAST                0 (x)
              8 LOAD_CONST               2 ('=10')
             10 FORMAT_VALUE             4 (with format)
             12 CALL_FUNCTION            1
             14 POP_TOP
             16 LOAD_CONST               0 (None)
             18 RETURN_VALUE
1
  • 9
    To clarify, the = specifies to put padding after the sign but before digits. Setting x to a negative value will show the difference between f"{x:10}" and f"{x:=10}". Commented Aug 14, 2023 at 18:52

Not the answer you're looking for? Browse other questions tagged or ask your own question.