String Formatting: % Vs. .Format Vs. F-String Literal

String formatting: % vs. .format vs. f-string literal

To answer your first question... .format just seems more sophisticated in many ways. An annoying thing about % is also how it can either take a variable or a tuple. You'd think the following would always work:

"Hello %s" % name

yet, if name happens to be (1, 2, 3), it will throw a TypeError. To guarantee that it always prints, you'd need to do

"Hello %s" % (name,)   # supply the single argument as a single-item tuple

which is just ugly. .format doesn't have those issues. Also in the second example you gave, the .format example is much cleaner looking.

Only use it for backwards compatibility with Python 2.5.


To answer your second question, string formatting happens at the same time as any other operation - when the string formatting expression is evaluated. And Python, not being a lazy language, evaluates expressions before calling functions, so the expression log.debug("some debug info: %s" % some_info) will first evaluate the string to, e.g. "some debug info: roflcopters are active", then that string will be passed to log.debug().

f-strings vs str.format()

I'm afraid that it will be deprecated during the next Python versions

Don't be, str.format does not appear (nor has a reason) to be leaving any time soon, the PEP that introduced fprefixed-strings even states in its Abstract:

This PEP does not propose to remove or deprecate any of the existing string formatting mechanisms.

Formatted strings were introduced to address some of the shortcomings other methods for formatting strings had; not to throw the old methods away and force god-knows how many projects to use f-string's if they want their code to work for Python 3.6+.


As for the performance of these, it seems my initial suspicion that they might be slower is wrong, f-strings seem to easily outperform their .format counterparts:

➜ cpython git:(master) ./python -m timeit -s "a = 'test'" "f'formatting a string {a}'"
500000 loops, best of 5: 628 nsec per loop
➜ cpython git:(master) ./python -m timeit "'formatting a string {a}'.format(a='test')"
100000 loops, best of 5: 2.03 usec per loop

These were done against the master branch of the CPython repository as of this writing; they are definitely subject to change:

  • f-strings, as a new feature, might have possible optimizations
  • Optimizations to CPython might make .format faster (e.g Speedup method calls 1.2x)

But really, don't worry about speed so much, worry about what is more readable to you and to others.

In many cases, that's going to be f-strings, but there's some cases where format is better.

Is there a difference between f and F in Python string formatting?

As explained in the PEP 498, chapter Specification, both are accepted, and should not differ.

In source code, f-strings are string literals that are prefixed by the letter 'f' or 'F'. Everywhere this PEP uses 'f', 'F' may also be used.

What's the difference between f{x} and {}.format(x) in python?

Python f-strings were added in 3.6. Therefore you should consider using format() if you need compatibility with earlier versions. Otherwise, use f-strings.

On macOS 12.1 running 3 GHz 10-Core Intel Xeon W and Python 3.10.2, f-strings are significantly faster (~60%)

f-string representation different than str()

From "Formatted string literals" in the Python reference:
f-strings are invoke the "format protocol", same as the format built-in function. It means that the __format__ magic method is called instead of __str__.

class Foo:
def __repr__(self):
return "Foo()"

def __str__(self):
return "A wild Foo"

def __format__(self, format_spec):
if not format_spec:
return "A formatted Foo"
return f"A formatted Foo, but also {format_spec}!"

>>> foo = Foo()
>>> repr(foo)
'Foo()'
>>> str(foo)
'A wild Foo'
>>> format(foo)
'A formatted Foo'
>>> f"{foo}"
'A formatted Foo'
>>> format(foo, "Bar")
'A formatted Foo, but also Bar!'
>>> f"{foo:Bar}"
'A formatted Foo, but also Bar!'

If you don't want __format__ to be called, you can specify !s (for str), !r (for repr) or !a (for ascii) after the expression:

>>> foo = Foo()
>>> f"{foo}"
'A formatted Foo'
>>> f"{foo!s}"
'A wild Foo'
>>> f"{foo!r}"
'Foo()'

This is occasionally useful with strings:

>>> key = 'something\n nasty!'
>>> error_message = f"Key not found: {key!r}"
>>> error_message
"Key not found: 'something\\n nasty!'"

Python string formatting: is '%' more efficient than 'format' function?

  1. Yes, % string formatting is faster than the .format method
  2. most likely (this may have a much better explanation) due to % being a syntactical notation (hence fast execution), whereas .format involves at least one extra method call
  3. because attribute value access also involves an extra method call, viz. __getattr__

I ran a slightly better analysis (on Python 3.8.2) using timeit of various formatting methods, results of which are as follows (pretty-printed with BeautifulTable) -

































































































Type \ num_vars1251050250
f_str_str0.0560.0630.1150.1730.7543.717
f_str_int0.0550.1480.3540.6563.18615.747
concat_str0.0120.0440.1690.3331.88810.231
pct_s_str0.0910.1140.1820.3131.2136.019
pct_s_int0.0900.1410.2480.4792.17910.768
dot_format_str0.1430.1570.2510.4611.7458.259
dot_format_int0.1410.1920.3330.6202.73513.298
dot_format2_str0.1590.1950.3300.6343.49418.975
dot_format2_int0.1580.2270.4220.7624.33725.498

Why does f-string literal not work here, whereas %()s formatting does?

"must be between %(min)s and %(max)s!" is a string literal that Flask will later perform a search-and-replace on, while f"must be between {min} and {max}!" is a simpler and more efficient way to say "must be between " + str(min) + " and " + str(max) + "!". That evaluates to the string you described.

Why were literal formatted strings (f-strings) so slow in Python 3.6 alpha? (now fixed in 3.6 stable)

Note: This answer was written for the Python 3.6 alpha releases. A new opcode added to 3.6.0b1 improved f-string performance significantly.


The f"..." syntax is effectively converted to a str.join() operation on the literal string parts around the {...} expressions, and the results of the expressions themselves passed through the object.__format__() method (passing any :.. format specification in). You can see this when disassembling:

>>> import dis
>>> dis.dis(compile('f"X is {x}"', '', 'exec'))
1 0 LOAD_CONST 0 ('')
3 LOAD_ATTR 0 (join)
6 LOAD_CONST 1 ('X is ')
9 LOAD_NAME 1 (x)
12 FORMAT_VALUE 0
15 BUILD_LIST 2
18 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
21 POP_TOP
22 LOAD_CONST 2 (None)
25 RETURN_VALUE
>>> dis.dis(compile('"X is {}".format(x)', '', 'exec'))
1 0 LOAD_CONST 0 ('X is {}')
3 LOAD_ATTR 0 (format)
6 LOAD_NAME 1 (x)
9 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
12 POP_TOP
13 LOAD_CONST 1 (None)
16 RETURN_VALUE

Note the BUILD_LIST and LOAD_ATTR .. (join) op-codes in that result. The new FORMAT_VALUE takes the top of the stack plus a format value (parsed out at compile time) to combine these in a object.__format__() call.

So your example, f"X is {x}", is translated to:

''.join(["X is ", x.__format__('')])

Note that this requires Python to create a list object, and call the str.join() method.

The str.format() call is also a method call, and after parsing there is still a call to x.__format__('') involved, but crucially, there is no list creation involved here. It is this difference that makes the str.format() method faster.

Note that Python 3.6 has only been released as an alpha build; this implementation can still easily change. See PEP 494 – Python 3.6 Release Schedule for the time table, as well as Python issue #27078 (opened in response to this question) for a discussion on how to further improve the performance of formatted string literals.

Python - convert format string to f-string

str.format() is usually fine, but if you really want an f-string then it would look like this:
message = f'{call_components["status"]} : {call_components["method"]} : {call_components["url"]}'



Related Topics



Leave a reply



Submit