问题
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 for my surprise, when I checked the velocity of both methods with the timeit function, I found out that
>>> 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)
回答1:
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.
来源:https://stackoverflow.com/questions/56587807/why-are-f-strings-faster-than-str-to-parse-values