239

I tried this code:

names = ['Adam', 'Bob', 'Cyril']
text = f"Winners are:\n{'\n'.join(names)}"
print(text)

However, '\' cannot be used inside the {...} expression portions of an f-string. How can I make it work? The result should be:

Winners are:
Adam
Bob
Cyril

See Why isn't it possible to use backslashes inside the braces of f-strings? How can I work around the problem? for some additional discussion of why the limitation exists.

Karl Knechtel
  • 62,466
  • 11
  • 102
  • 153
malmed
  • 2,698
  • 2
  • 13
  • 11

8 Answers8

255

Python 3.12+

You can use backslashes within f-strings and the existing code from the question works as expected. See https://docs.python.org/3.12/whatsnew/3.12.html#pep-701-syntactic-formalization-of-f-strings.

Python < 3.12

You can't. Backslashes cannot appear inside the curly braces {}; doing so results in a SyntaxError:

>>> f'{\}'
SyntaxError: f-string expression part cannot include a backslash

This is specified in the PEP for f-strings:

Backslashes may not appear inside the expression portions of f-strings, [...]

One option is assigning '\n' to a name and then .join on that inside the f-string; that is, without using a literal:

names = ['Adam', 'Bob', 'Cyril']
nl = '\n'
text = f"Winners are:{nl}{nl.join(names)}"
print(text)

Results in:

Winners are:
Adam
Bob
Cyril

Another option, as specified by @wim, is to use chr(10) to get \n returned and then join there. f"Winners are:\n{chr(10).join(names)}"

Yet another, of course, is to '\n'.join beforehand and then add the name accordingly:

n = "\n".join(names)
text = f"Winners are:\n{n}"

which results in the same output.

Note:

This is one of the small differences between f-strings and str.format. In the latter, you can always use punctuation granted that a corresponding wacky dict is unpacked that contains those keys:

>>> "{\\} {*}".format(**{"\\": 'Hello', "*": 'World!'})
"Hello World!"

(Please don't do this.)

In the former, punctuation isn't allowed because you can't have identifiers that use them.


Aside: I would definitely opt for print or format, as the other answers suggest as an alternative. The options I've given only apply if you must for some reason use f-strings.

Just because something is new, doesn't mean you should try and do everything with it ;-)

tripleee
  • 175,061
  • 34
  • 275
  • 318
Dimitris Fasarakis Hilliard
  • 150,925
  • 31
  • 268
  • 253
  • Does not make sense to me: `nl = '\n' text = f"Winners are:{nl}"`. Use: text = f"Winners are:\n" – Timo Jan 07 '21 at 19:10
  • @Timo OP needed to `join` on `\n` in a format string, specifically in the embedded expression. – Dimitris Fasarakis Hilliard Jan 08 '21 at 08:15
  • 1
    I believe @timo 's point was the first `{nl}` wasn't necessary, as the `\n` isn't within the expression portion of the f-string. `text = f"Winners are:\n"` is perfectly fine, it is the `{"\n".join()}` portion that causes issues within an f-string. – Sam Jan 03 '22 at 23:59
  • 1
    Any idea why f-strings are limited in this way? In JavaScript, for example, there is no such limitation on the expressions you can place inside a string template. – Ron Inbar Mar 10 '22 at 15:06
  • @RonInbar in Python there's other two ways to format strings which don't give that limitation and they already existed before f-strings, which are `'%s' % ('\n')` and `'{}'.format('\n')` – Iuri Guilherme Jul 01 '22 at 21:59
84

You don't need f-strings or other formatters to print a list of strings with a separator. Just use the sep keyword argument to print():

names = ['Adam', 'Bob', 'Cyril']
print('Winners are:', *names, sep='\n')

Output:

Winners are:
Adam
Bob
Cyril

That said, using str.join()/str.format() here would arguably be simpler and more readable than any f-string workaround:

print('\n'.join(['Winners are:', *names]))
print('Winners are:\n{}'.format('\n'.join(names)))
Eugene Yarmash
  • 142,882
  • 41
  • 325
  • 378
  • 14
    Best answer so far. I use star unpacking in the print function all the time these days to have a look inside some object, e.g. `print(*dir(some_object), sep='\n')` or `print(*vars(some_object), sep='\n')`. – Rick Jun 27 '17 at 13:41
  • 2
    What do you do if you don't want to print the list directly, e.g. if you're passing it to a logger? – bob Feb 03 '20 at 15:47
  • 1
    @bob: then just use `str.join()`: `text = '\n'.join(['Winners are:', *names])`. BTW, [`print()`](https://docs.python.org/3/library/functions.html#print) can be used to write to _any file_ (specified with the `file` argument, `sys.stdout` by default). – Eugene Yarmash Feb 03 '20 at 20:38
24

You can't use backslashes in f-strings as others have said, but you could step around this using os.linesep (although note this won't be \n on all platforms, and is not recommended unless reading/writing binary files; see Rick's comments):

>>> import os
>>> names = ['Adam', 'Bob', 'Cyril']
>>> print(f"Winners are:\n{os.linesep.join(names)}")
Winners are:
Adam
Bob
Cyril 

Or perhaps in a less readable way, but guaranteed to be \n, with chr():

>>> print(f"Winners are:\n{chr(10).join(names)}")
Winners are:
Adam
Bob
Cyril
Chris_Rands
  • 38,994
  • 14
  • 83
  • 119
  • 2
    Wasn't me, but using `os.linesep` is [not a good idea](https://docs.python.org/3/library/os.html?highlight=linesep#os.linesep) when writing text. – Rick Jun 27 '17 at 13:30
  • 1
    @RickTeachey I did already add the caveat in parentheses and suggest an alternative approach. Anyway, the OP is printing to screen, not writing to a file opened in text mode – Chris_Rands Jun 27 '17 at 13:34
  • I don't think it matters. `os.linesep` is just [for reading, or reading and writing in binary mode](https://stackoverflow.com/questions/38074811/what-is-os-linesep-for). I know it will work the same in this case, but it's a bad habit to start. But again: I wasn't the downvote. The caveat is good enough for me. :) – Rick Jun 27 '17 at 13:45
11

The other answers give ideas for how to put the newline character into a f-string field. However, I would argue that for the example the OP gave (which may or may not be indicative of OP's actual use case), none of these ideas should actually be used.

The entire point of using f-strings is increasing code readability. There is nothing you can do with f-strings that you cannot do with format. Consider carefully whether there is anything more readable about this (if you could do it):

f"Winners are:\n{'\n'.join(names)}"

...or this:

newline = '\n'
f"Winners are:\n{newline.join(names)}"

...or this:

"Winners are:\n{chr(10).join(names)}"

vs. this:

"Winners are:\n{}".format('\n'.join(names))

The last way is at least as readable, if not more so.

In short: don't use a hammer when you need a screwdriver just because you have a shiny new one. Code is read much more often than it is written.

For other use cases, yes, it's possible the chr(10) idea or newline idea may be appropriate. But not for the one given.

Rick
  • 43,029
  • 15
  • 76
  • 119
  • 12
    Readability is subjective :) ... old practice suits skilled ones and can be more readable in some cases, but can be almost unknown to noobs and hence unreadable for them. Sorry for philosophical point of view. – malmed Jun 30 '17 at 00:02
  • 2
    @malmed Readability generally is not subjective. Definitely not in this case. But it's not worth arguing about at length. – Rick Jun 30 '17 at 00:57
  • 1
    @malmed You're right that readability is "subjective" in the sense that it is *trainable* by prior experience. But because our brains and senses have limitations, readability can be objectively measured in terms of how physically easy it is to scan the relevant text, how often human brains tend to pattern-match it correctly, how correctly it hints to our brain predictions about other code (including the beginning of a statement/line hinting at what the end will be), and how readily it becomes more readable to a novice. – mtraceur Jan 14 '19 at 18:39
  • The notion that one will sped one's career reading code that consists of only the latest-up-to-datest syntax is extremely optimitic. – snakecharmerb Feb 09 '23 at 22:10
  • 1
    @snakecharmerb for many, sure. but in the scientific python world, for example, people to tend to use only the latest syntax. technical debt is much less of a thing. but you do have a point. – Rick Feb 10 '23 at 16:45
3
print(f'{"blah\n"}')

The above statement will raise SyntaxError, But to avoid the error, you can simply assign the string containing \n to a variable and use it in f-string.

x = "blah\n"
print(f'{x}')
CrackerKSR
  • 1,380
  • 1
  • 11
  • 29
3

On Python 3.12+, you can:

>>> names = ['Adam', 'Bob', 'Cyril']
>>> print(f"Winners are:\n{'\n'.join(names)}")
Winners are:
Adam
Bob
Cyril

You can also reuse the same quote in the expression part of an f-string:

>>> print(f"Winners are:\n{"\n".join(names)}")        
Winners are:
Adam
Bob
Cyril

See PEP 701 for details.

Boris Verkhovskiy
  • 14,854
  • 11
  • 100
  • 103
1

If (and only if!) readability is the top priority, and speed is truly not a factor, f-strings are very useful to make a simple function self documenting even if there are simpler ways to program it. Readability is maximized with f-strings when: (1) the statements that change the argument's state are clearly obvious, and (2) when the arguments are printed, the print statement is carefully formatted, and visually presented to make the arguments stand out:

'''
function to print out an identification header, consisting
of the programmer supplied title, lastname, and firstname:
''' 
FORMAT_DATE = "%m-%d-%y %H:%M:%S"

date_and_time = datetime.now()
name_line = f"* {lastname}, {firstname}"
title_line = f"* {title}"
date_line = f"* {date_and_time.strftime(FORMAT_DATE)}"
print(name_line
    + '\n'
    + title_line
    + '\n'
    + date_line)

output:

* Lovelace, Ada
* Bernoulli Numbers algorithm implemented in Python
* 10-28-42 20:13:22
HK boy
  • 1,398
  • 11
  • 17
  • 25
1

And remember not to use this strange way that also works:

names = ['Adam', 'Bob', 'Cyril']
text = f"""{'''
'''.join(names)}"""
print(text)
Pavel Shishpor
  • 742
  • 6
  • 14