20

In Python >=3.6, f-strings can be used as a replacement for the str.format method. As a simple example, these are equivalent:

'{} {}'.format(2+2, "hey")
f'{2+2} {"hey"}'

Disregarding format specifiers, I can basically move the positional arguments of str.format inside braces in an f-string. Note specifically that I am allowed to just put str literals in here, although it may seem a bit unwieldy.

There are however some limitations. Specifically, backslashes in any shape or form are disallowed inside the braces of an f-string:

'{}'.format("new\nline")  # legal
f'{"new\nline"}'          # illegal
f'{"\\"}'                 # illegal

I cannot even use \ to split up a long line if it's inside the braces;

f'{2+\
2}'     # illegal

even though this usage of \ is perfectly allowed inside normal str's;

'{\
}'.format(2+2)  # legal

It seems to me that a hard stop is coded into the parser if it sees the \ character at all inside the braces of an f-string. Why is this limitation implemented? Though the docs specify this behavior, it does not justify why.

Karl Knechtel
  • 62,466
  • 11
  • 102
  • 153
jmd_dk
  • 12,125
  • 9
  • 63
  • 94
  • 5
    See [Let’s make escaping in f-literals impossible](https://mail.python.org/pipermail/python-ideas/2016-August/041727.html) and the conclusion [Changes to PEP 498 (f-strings)](https://mail.python.org/pipermail/python-dev/2016-August/145979.html) – miradulo Aug 09 '18 at 21:30
  • I too think this violates 'Principle of Least Astonishment'. Actually I was surprised that it didn't accept "\n" character. I write Ruby code for living and you can write all sorts of syntactically correct Ruby code in `#{}` interpolation – RajaRaviVarma Aug 20 '18 at 07:07
  • Starting with Python 3.12, it [is possible](https://docs.python.org/3.12/whatsnew/3.12.html#pep-701-syntactic-formalization-of-f-strings). – Boris Verkhovskiy Jun 11 '23 at 02:19

4 Answers4

14

You seem to expect

'{}'.format("new\nline")

and

f'{"new\nline"}'

to be equivalent. That's not what I would expect, and it's not how backslashes in f-strings worked back in the pre-release versions of Python 3.6 where backslashes between the braces were allowed. Back then, you'd get an error because

"new
line"

is not a valid Python expression.

As just demonstrated, backslashes in the braces are confusing and ambiguous, and they were banned to avoid confusion:

The point of this is to disallow convoluted code like:

>>> d = {'a': 4}
>>> f'{d[\'a\']}'
'4'

In addition, I'll disallow escapes to be used for brackets, as in:

>>> f'\x7bd["a"]}'
'4'

(where chr(0x7b) == "{").

user2357112
  • 260,549
  • 28
  • 431
  • 505
  • 4
    Agreed backslashes in braces are confusing, but backslashes outside are not permitted either and that is too bad. This example is not confusing: `f"Make your selection \n A- {textA} \n B- {textB} \n C- {textC}"`. – Guimoute Mar 29 '19 at 09:10
  • @Guimoute: But then the question arises of whether `\{` or `\x7b` start a replacement field. – user2357112 Mar 29 '19 at 14:34
  • True. Maybe only `\n`, `\t` and `\r` could be allowed. Surprisingly I just found out that `f"/!\\ caution"` raises no error and it prints fine. – Guimoute Mar 29 '19 at 14:42
  • 3
    @Guimoute Your non-confusing example is perfectly legal (in Python 3.7.8, at least): `f"Make your selection \n A- {textA} \n B- {textB} \n C- {textC}"`. I believe backslashes are only disallowed *inside* braces. – Steven Oxley May 20 '21 at 14:54
  • @StevenOxley Yes, I have no problem with that example now. Unfortunately I have no idea whether that was the case at the time of the original comment, 2+ years ago. – Guimoute May 20 '21 at 18:49
11

It's annoying that you can't do this:

things = ['Thing one','Thing two','Thing three']
print(f"I have a list of things: \n{'\n'.join(things)}")

But you can do this:

things = ['Thing one','Thing two','Thing three']
nl = '\n'
print(f"I have a list of things:\n{nl.join(things)}")
vy32
  • 28,461
  • 37
  • 122
  • 246
  • Also the use of textwrap.dedent is totally broken for multi-line f-strings, as the default case is to begin the dedent with a line-continuation `dedent('''\\`. This is plain broken imo and the bizarre purist attitudes like this from the py3 devs only serve to hurt the user. – Rebs Apr 22 '21 at 04:41
  • @Rebs: Beginning an f-string with a line continuation works fine: https://ideone.com/OM7Fxd – user2357112 Jun 27 '23 at 08:22
  • Starting with Python 3.12 your first code snippet [works](https://peps.python.org/pep-0701/) – Boris Verkhovskiy Jul 01 '23 at 03:41
4

For new lines, you can use os.linesep instead of \n. For example:

>>> import os
>>> 
>>> print(f"Numbers:\n{os.linesep.join(map(str, [10, 20, 30]))}")
Numbers:
10
20
30
Giorgos Myrianthous
  • 36,235
  • 20
  • 134
  • 156
  • 2
    The correct line separator for `print` is `'\n'` on all platforms, but on Windows `os.linesep` is `'\r\n'` ([ref](https://docs.python.org/3/library/os.html#os.linesep)). I think `'\n' == chr(10)` is guaranteed, so you can write `chr(10).join(...)`, though it feels hacky. – benrg Sep 30 '21 at 00:12
1

I am not sure if this helps, but instead of the illegal

f'{"new\nline"}'

one could use

f'{"new"+chr(10)+"line"}'
karpan
  • 421
  • 1
  • 5
  • 13