Since I discovered f-strings, it has become one of my favourite python features that I use every day. F-strings are syntactic sugar for string formatting, introduced in python version 3.6. The language addition allows for more readable, more concise, and less error prone code when dealing with strings.
In the old way of defining strings, the %s symbol is used for variable placeholders inside the string, with variables passed as a tuple at the end. This becomes hard to read when working with many variables to create a long string, as you have to count the placeholders to understand which value will be inserted into the string.
# old way
greeting = 'hello'
greeted = 'world'
greeter = 'matt'
result = "%s, %s! -%s" % (greeting, greeted, greeter)
>>> print(result)
hello, world! -matt
Using the new syntax, the %s placeholder is replaced by an inline evaluation surrounded by curly braces. Note that the string must be prepended by an 'f'. This syntax works with single quotes ('), double quotes ("), and docstrings (""").
# using f-strings
result = f'{greeting}, {greeted}! -{greeter}'
>>> print(result)
hello, world! -matt
F-strings can be nested and combined with other operations such as list comprehensions and joins for all kinds of string manipulation. This becomes hard to read with so much going on, but is still more legible than using placeholders
greeted = ('zapread', 'world')
end = '\ngoodbye!'
# old way
result = "%s! -%s" % ('! '.join(["%s, %s" % (greeting, name) for name in greeted]), "%s%s" % (greeter, end))
# using f-strings
result = f"{'! '.join([f'{greeting}, {name}' for name in greeted])}! -{f'{greeter}{end}'}"
>>> print(result)
hello, zapread! hello, world! -matt
goodbye!
In python 3.8 is even better
>>> my_name = 'John Doe'
>>> print(f'{my_name = }')
my_name = 'John Doe'