Strings in Python can't be changed. text += piece makes a new string holding the old text plus the piece, so a loop that grows a string can end up copying the same characters over and over. "".join(pieces) works out the final length once and copies each piece exactly once.
Measure it
Run this. The numbers are yours, from your own machine.
import timeit words = ["reading"] * 20_000 def with_plus(): text = "" for w in words: text += w + "," return text def with_join(): return ",".join(words) + "," t_plus = timeit.timeit(with_plus, number=20) t_join = timeit.timeit(with_join, number=20) print(f"+= : {t_plus * 1000:.1f} ms join: {t_join * 1000:.1f} ms") print(f"join was about {t_plus / t_join:.0f}x faster")
+= : 44.5 ms join: 4.5 ms join was about 10x faster
About 10× on my machine, for 20,000 short words. The gap is not fixed: it depends on how long the pieces are and on your Python.
The pattern in real code
Most of the time the string is built from a loop over records. Collect the parts in a list, then join once at the end.
orders = [("A-1001", 49.99), ("A-1002", 12.50), ("A-1003", 5.00)] lines = [] for order_id, amount in orders: lines.append(f"{order_id}: £{amount:.2f}") report = "\n".join(lines) print(report)
A-1001: £49.99 A-1002: £12.50 A-1003: £5.00
Or, when each part comes straight out of the loop, a generator expression inside join does the same job in one line: "\n".join(f"{i}: {a:.2f}" for i, a in orders).
Why it works
join makes two passes: one to add up the lengths, one to copy. += has no idea more pieces are coming, so every step has to produce a complete, finished string. CPython sometimes cheats and grows the string in place when nothing else refers to it, which is why the gap here is 10× and not far worse. That shortcut isn't guaranteed: it depends on the interpreter and on whether another name still points at the old string, so don't write code that relies on it.
When not to use it
For two or three pieces, a + b or an f-string is clearer and just as fast; nobody needs "".join([first, " ", last]). The rule is about loops, where the number of pieces grows with the data.