Reputation: 1555
Let's say I have two variables:
>>> a = "hello"
>>> b = "world"
I can concatenate them in two ways; using +
:
>>> a + b
"helloworld"
Or using an f-string:
>>> f"{a}{b}"
"helloworld"
Which way is better or a better practice? Someone told me the f-string is better practice in terms of performance and robustness, and I'd like to know why in detail.
Upvotes: 34
Views: 20301
Reputation: 39
The top two answers got it wrong. The true reason is as the number of strings grow, using f string becomes significantly faster. As you can see below, the difference is quite big. And this will increase as we increase the number of concatenations.
>>> timeit('f"{a}{b}{c}{d}{e}{f}{g}{h}"', setup='a, b, c, d, e, f, g, h = ("hello" * 100,) * 8')
0.22236634399996547
>>> timeit('a + b + c + d + e + f + g + h', setup='a, b, c, d, e, f, g, h = ("hello" * 100,) * 8')
0.48515824200148927
Upvotes: -1
Reputation: 353
depends on what you need. But mostly f-string is better
reason: 1, It's clear and short
# if you have 2 to join:
a + b
f"{a}{b}"
# if you have 4 to join:
f"this is {a} and {b}, also {c} and {d}"
"this is" + a + "and" + b + "also" + c + "and" + d
2, It's faster, and easier to use
# example: it allows mult-line
f"""
this{a}, this{b}
and this{c}
"""
# example: don't need type convert
"this is: " + str(a)
f"this is: {a}"
but, as I say, depends on what you need
Upvotes: 0
Reputation: 91
If you have more strings (>2 for 1 char each) to concatenate, f-strings performs better:
>>> from timeit import timeit
>>> timeit('a+b', setup='a,b = "h", "e"')
0.05678774899979544
>>> timeit('f"{a}{b}"', setup='a,b = "h", "e"')
0.09656870200024059
>>> timeit('a+b+c', setup='a,b,c = "h", "e", "l"')
0.09475198700010878
>>> timeit('f"{a}{b}{c}"', setup='a,b,c = "h", "e", "l"')
0.08498188300018228
>>> timeit('a+b+c+d', setup='a,b,c,d = "h", "e", "l", "l"')
0.13406166100003247
>>> timeit('f"{a}{b}{c}{d}"', setup='a,b,c,d = "h", "e", "l", "l"')
0.09481844199990519
>>> timeit('a+b+c+d+e', setup='a,b,c,d,e = "h", "e", "l", "l","o"')
0.21804361799991057
>>> timeit('f"{a}{b}{c}{d}{e}"', setup='a,b,c,d,e = "h", "e", "l", "l","o"')
0.11850353900013033
Upvotes: 9
Reputation: 2633
Performance wise, I would have expected the format string literal to be significantly faster than string concatenation however I was shocked to find this was not the case.
I used the timeit
module to test how long it took format string literals vs. string concatenation. I tested strings of length 10 to 1 million characters.
from timeit import timeit
import matplotlib.pyplot as plt
n = 1000000000
setup = """\
a = 'a'*{str_len}
b = 'b'*{str_len}
"""
fstr_stmt = """\
f'{a}{b}'
"""
concat_stmt = """\
a+b
"""
str_lens = [10, 100, 1000, 10000, 100000, 1000000]
fstr_t = []
concat_t = []
for str_len in str_lens:
n_iters = n//str_len
fstr_t.append(timeit(setup=setup.format(str_len=str_len), stmt=fstr_stmt, number=n_iters)/n_iters)
concat_t.append(timeit(setup=setup.format(str_len=str_len), stmt=concat_stmt, number=n_iters)/n_iters)
ratio = fstr_t[-1]/concat_t[-1]
print(f"For two strings of length {str_len:7d}, concatenation is {ratio:.5f} times faster than f-strings")
plt.plot(str_lens, fstr_t, "r*-")
plt.plot(str_lens, concat_t, "c*-")
plt.xscale("log")
plt.yscale("log")
plt.xlabel("String length (log scale)")
plt.ylabel("Seconds per iteration (log scale)")
plt.grid()
plt.show()
Console Output:
For two strings of length 10, concatenation is 1.06938 times faster than f-strings
For two strings of length 100, concatenation is 1.14887 times faster than f-strings
For two strings of length 1000, concatenation is 1.13994 times faster than f-strings
For two strings of length 10000, concatenation is 1.26934 times faster than f-strings
For two strings of length 100000, concatenation is 1.21585 times faster than f-strings
For two strings of length 1000000, concatenation is 1.01816 times faster than f-strings
And the plot:
Summary: Using the string concatenation operator is slightly faster than using format string literals. Unless you are performing many hundreds of thousands of string concatenations and need them done very quickly, the implementation chosen is unlikely to make a difference.
From a readability standpoint, f-string literals are more aesthetically pleasing and easier to read than string concatenation. Also, as the answer from Daniel points out, f-strings are able to handle inputs of different types while using +
requires both objects to be strings (or the overloading of the __add__
, and __radd__
methods).
Edit: As chepner points out in their comment, using f-strings is more efficient when more than two strings are involved. For example, adding another variable, c
, to the setup and timeit
statements yields the following console output:
For three strings of length 10, concatenation is 0.77931 times faster than f-strings
For three strings of length 100, concatenation is 0.67699 times faster than f-strings
For three strings of length 1000, concatenation is 0.60220 times faster than f-strings
For three strings of length 10000, concatenation is 1.27484 times faster than f-strings
For three strings of length 100000, concatenation is 0.98911 times faster than f-strings
For three strings of length 1000000, concatenation is 0.60201 times faster than f-strings
Upvotes: 33
Reputation: 3756
Well method 2 is a clearer way to express that you want the strings to be concatenated together. That is why I would suggest using that method in the case you've provided.
The problem with method one is when you start using different Types.
# integers
a = 1
b = 2
a + b # 3
f'{a}{b}' # 12
# lists
a = [2, 3]
b = [5, 6]
a + b # [2, 3, 5, 6]
f'{a}{b}' # [2, 3][5, 6]
# dicts
a = {'a': 3}
b = {'a': 5}
a + b # TypeError
f'{a}{b}' # {'a': 3}{'b': 5}
When the values are anything but strings you will get a different results. I can't speak to performance, but depending on how many times the line is executed it could have almost no difference.
Upvotes: 1
Reputation: 51122
There are two aspects to this: performance and convenience.
Using timeit
in Python 3.8.0, I get that concatenation using an f-string is consistently slower than +
, but the percentage difference is small for longer strings:
>>> from timeit import timeit
>>> timeit('a + b', setup='a, b = "hello", "world"')
0.059246900000289315
>>> timeit('f"{a}{b}"', setup='a, b = "hello", "world"')
0.06997206999949412
>>> timeit('a + b', setup='a, b = "hello"*100, "world"*100')
0.10218418099975679
>>> timeit('f"{a}{b}"', setup='a, b = "hello"*100, "world"*100')
0.1108272269993904
>>> timeit('a + b', setup='a, b = "hello"*10000, "world"*10000')
2.6094200410007033
>>> timeit('f"{a}{b}"', setup='a, b = "hello"*10000, "world"*10000')
2.7300010479993944
However, the f-string may be a bit more convenient when your inputs aren't already strings:
>>> a, b = [1, 2, 3], True
>>> str(a) + str(b)
'[1, 2, 3]True'
>>> f'{a}{b}'
'[1, 2, 3]True'
Upvotes: 11