Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why are f-strings faster than str() to parse values?

I was playing around with f-strings (see PEP 498), and I decided to check the speed of the f-string parse, (e.g. f"{1}") in comparison with the usual str parse (e.g str(1)). But to my surprise, when I checked the speed of both methods with the timeit function, I found out that f-strings are faster.

>>> from timeit import timeit
>>> timeit("f'{1}'")
0.1678762999999961

whereas

>>> timeit("str(1)")
0.3216999999999999

or even the repr func, which in most of the cases is faster than str cast

>>> timeit("repr(1)")
0.2528296999999995

I wonder why is that? I thought that the f-strings called str internally, but now, I'm a bit confused, any ideas? Thanks in advance!

PD: Just if anyone is wondering:

assert f"{1}" == str(1) == repr(1)
like image 714
Davichete Avatar asked Jun 13 '19 20:06

Davichete


People also ask

Are F strings faster?

As of Python 3.6, f-strings are a great new way to format strings. Not only are they more readable, more concise, and less prone to error than other ways of formatting, but they are also faster!

What is the difference between F-string and string?

F-strings provide a way to embed expressions inside string literals, using a minimal syntax. It should be noted that an f-string is really an expression evaluated at run time, not a constant value. In Python source code, an f-string is a literal string, prefixed with 'f', which contains expressions inside braces.

Are F strings better than format?

Summary: f-string is more readable and easier to implement than % and . format() string formatting styles. Furthermore, using f-strings is suggested for Python 3.6 and above while . format() is best suited for Python 2.6 and above.

Why do we use F-string?

The most obvious usage of F strings is to interpolate simple values inside of throws, output, what have you. This is done using the {} syntax and just providing that values name. The value will also be implicitly casted if it is possible, of course.


1 Answers

The simple answer is because f-strings are part of the language's grammar and syntax. The str() call on the other hand requires a symbol table lookup, followed by a function call.

Here's a similar example which interpolates an integer variable, contrast this with the constant value interpolation.

x = 1

%timeit f'{1}'
%timeit f'{x}'
%timeit str(1)
%timeit str(x)

113 ns ± 2.25 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
166 ns ± 4.71 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
342 ns ± 23.9 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
375 ns ± 11.5 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

The difference in the behaviour is obvious when you look at the disassembled byte code with dis.

import dis

dis.dis("f'{x}'")
  1           0 LOAD_NAME                0 (x)
              2 FORMAT_VALUE             0
              4 RETURN_VALUE

dis.dis("str(x)")
  1           0 LOAD_NAME                0 (str)
              2 LOAD_NAME                1 (x)
              4 CALL_FUNCTION            1
              6 RETURN_VALUE

The heavy lifting is all in the CALL_FUNCTION instruction, an overhead which f-strings certainly don't have -- at least in this case, as nothing needs to be eval'd.

like image 141
cs95 Avatar answered Oct 04 '22 07:10

cs95