f-strings vs str.format()
Asked Answered
C

6

102

I'm using the .format() a lot in my Python 3.5 projects, but I'm afraid that it will be deprecated during the next Python versions because of f-strings, the new kind of string literal.

>>> name = "Test"
>>> f"My app name is {name}."
'My app name is Test.'

Does the formatted string feature come to fully replace the old .format()? And from now on, would it be better to use the new style in all cases?

I understand that it's based on the idea that "Simple is better than complex." However, what about performance issues; is there any difference between them? Or is it just a simple look of the same feature?

Cally answered 30/3, 2017 at 16:25 Comment(5)
wait a minute, who says .format() will be depreciated? I doubt thatSunlight
No one, I agree. one of the things that I'm wondering about.. for future use.Cally
After Python 3's initial release, the community learned the hard way exactly how painful it is to introduce compatibility-breaking changes into new versions, and I don't think they're going to repeat that decision again lightly. Maybe when Python 4 comes out in 1,983 years...Dowel
@Dowel thanks. I wish so.Cally
Probably worth reading PEP 498 if you want the full version of the reasoning behind the feature.Parkinson
S
100

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.

Sumner answered 30/3, 2017 at 16:33 Comment(4)
What about performance improvements ? Any different in the internal implementation?Cally
@Cally and Jim, I did a brief speed investigation, however it would be interesting to poke around the source code, I'd bet they actually share a lot of code between them even though the bytecode produced by dis is relatively different. If I were to guess, as the string becomes longer and has more complicated formatting rules, the difference between the two will shrink.Parkinson
@Parkinson Good point. I would like to see the results published here ;)Cally
I find your speed comparisons interesting, as it is significantly different on my machine (Python 3.6.2 from Anaconda): the fstring version takes 0.0845usec while the format version takes 0.555usec. So while you see a factor ~3, I see a factor ~6. Odd.Foremast
P
39

To build on Jim's answer and address your performance concern, I used python's dis module to compare the bytecode instructions for two syntactically different, but functionally equivalent functions.

import dis

def f1():
    a = "test"
    return f"{a}"

def f2():
    return "{a}".format(a='test')

print(dis.dis(f1))
print(dis.dis(f2))

The result of which is:

 11           0 LOAD_CONST               1 ('test')
              2 STORE_FAST               0 (a)

 12           4 LOAD_FAST                0 (a)
              6 FORMAT_VALUE             0
              8 RETURN_VALUE
None
 15           0 LOAD_CONST               1 ('{a}')
              2 LOAD_ATTR                0 (format)
              4 LOAD_CONST               2 ('test')
              6 LOAD_CONST               3 (('a',))
              8 CALL_FUNCTION_KW         1
             10 RETURN_VALUE
None

One can see that the f-string handles the formatting without attribute or function calls, which can impose type checking and memory overhead. According to timeit this results in a roughly 3x performance gain (for my specific functions)

>>> timeit.timeit('f1()', 'from __main__ import f1', number=100000)
0.012325852433775708
>>> timeit.timeit('f2()', 'from __main__ import f2', number=100000)
0.036395029920726074
Parkinson answered 30/3, 2017 at 17:10 Comment(2)
@Parkinson What this dis really do? Also; how do you get those info from that printed texts?Teratoid
@Teratoid dis is short for disassemble. It takes a the bytecode of a compiled function, and decodes the python virtual machine instructions (binary data) into human readable words. The python virtual machine is a stack based vm so all the "LOAD" and "STORE" commands refer to pushing and pulling data from the stack. More information on the dis library can be found hereParkinson
R
26

One thing not mentioned, which makes deprecation of the old techniques impossible, is that interpolation is for string literals only. Meaning, the string is rendered once at runtime, and the template is not available to be used again with updated variables. As you would for example:

>>> str_template = '{i} squared: {n}'
>>> for i in range(2, 5):
...     print(str_template.format(i=i, n=i**2))
... 
2 squared: 4
3 squared: 9
4 squared: 16

Another case is i18n, where string.Template is used. Many use cases would be impossible without the older techniques. Enjoy string interpolation, but it is not available to every use case, i.e. places where you need a reusable template.

Ribosome answered 25/9, 2017 at 22:27 Comment(0)
A
5

I doubt str.format() would be deprecated as that would be a nightmare for existing projects. That being said...

string = f'This is a {object}'

is easier to read than

string = 'This is a {}'.format(object)

So I say use f-strings whenever you can.

Asseverate answered 15/4, 2021 at 16:59 Comment(0)
E
1

If you want to keep supporting python 3.5 you could use fstring

pip install fstring

from fstring import fstring

x = 1

y = 2.0

plus_result = "3.0"

print fstring("{x}+{y}={plus_result}")

# Prints: 1+2.0=3.0
Enrollment answered 22/4, 2018 at 6:32 Comment(1)
isn't better to use .format instead? this fstring is not straight port of python 3.6 f-strings and not support formattingSchober
M
-1

Another capability of f-strings that str.format() does not have is Expression evaluation:

>>> b = b'hello'
>>> f'b = 0x{b.hex()}'
'b = 0x68656c6c6f'
>>> 'b = 0x{.hex()}'.format(b)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'bytes' object has no attribute 'hex()'

str.format only supports attribute or index access

Manvil answered 27/4 at 10:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.