PyPython example

How to join a list into a string in Python

4 min read▶ Runs in an isolated hosted runtimeUpdated Jul 2026

Quick answer

Call join on the separator, not on the list: ", ".join(items). If the list holds anything other than strings, convert first — ", ".join(map(str, nums)).

str.join() is the one right way to turn a list into a string: it walks the iterable once, measures the total length, and allocates the result once. The two things that trip people up are the "backwards" syntax — the separator owns the method — and the TypeError you get the moment a non-string sneaks into the list. Both are covered below, and every example runs on this page.

1The separator joins the listRecommended

join is a method on str, so you call it on the glue and pass the list: separator.join(iterable). It reads backwards at first, but it's what lets any string join any iterable — a list, a tuple, a generator, even another string.

join_list.py

Output

Prints red, green, blue, then red | green | blue, then redgreenblue. An empty separator concatenates with nothing between — that's the standard way to rebuild a string from a list of characters.

2Joining numbers and other non-strings

join refuses to guess how to stringify your objects, so a list of ints raises TypeError: sequence item 0: expected str instance, int found. The index in that message is the first offending item, which is handy when only one element is the odd one out. Fix it with map(str, ...) or a generator expression:

join_numbers.py

Output

The first line prints TypeError: sequence item 0: expected str instance, int found; both fixes print 1, 2, 3. map(str, ...) is marginally faster and shorter; the generator wins as soon as you need real formatting per item, e.g. f"{n:.2f}".

3Joining with a newline

"\n".join(lines) is the idiomatic way to build multi-line output — one print(), one write, no trailing blank line. Nest a second join inside a generator and you have a CSV-ish table in two lines:

join_lines.py

Output

The inner join builds each row, the outer one stitches the rows together — ada | 1945 then linus | 1969. For real CSV with quoting and escaping, use the csv module instead of hand-rolling it.

4Building the string in a loop

You can append with += and track the separator yourself. It works, and the result is identical — but strings are immutable, so in principle every += copies the whole accumulated string, making the loop quadratic in the number of items. CPython has an in-place optimisation that often hides this, but it only applies when the string has a single reference and it is not part of the language spec, so it can vanish without warning.

join_loop.py

Output

Prints build, run, share and then True — same output, five extra lines, and an if i: guard purely to avoid a leading separator. join handles the "n − 1 separators" problem for free. If you really must accumulate incrementally, append to a list and join it at the end.

5Which should you use?

MethodHandles non-stringsSpeedBest for
", ".join(items)NoFastestLists that are already strings
", ".join(map(str, items))YesFastNumbers and mixed types
", ".join(str(x) for x in items)YesSlightly slowerFormatting each item
result += item (loop)Needs str()QuadraticNothing — prefer join()

6Common variation: an Oxford-comma join

User-facing text usually wants a, b, and c rather than a, b, c. Join everything except the last item, then attach the last one with and — with a special case for one or two items:

humanize.py

Output

Prints Python, Python and Go, Python, Go, and Rust, and Python, Go, Rust, and C++. The str(i) pass up front means it never raises the TypeError from section 2. Drop the comma before and if your style guide says so.

Frequently asked questions

Why is the separator first in Python’s join?

join is a method on str, not on list, so it is called as separator.join(iterable). That way one method on the string type can join any iterable — a list, tuple, set, or generator — instead of every sequence type needing its own join.

How do I join a list of numbers into a string?

Convert them first: ", ".join(map(str, nums)) or ", ".join(str(n) for n in nums). Calling ", ".join([1, 2, 3]) directly raises TypeError: sequence item 0: expected str instance, int found — the number in the message is the index of the first non-string item.

Is join() faster than adding strings in a loop?

Yes. join walks the iterable once, sums the lengths, and allocates the result string a single time, so it is O(n). Repeated += conceptually copies the whole accumulated string each iteration, which is quadratic; CPython has an in-place optimisation that often hides this, but it is not guaranteed by the language.

What does join() do with a string, a dict, or an empty list?

A string is an iterable of characters, so "-".join("abc") gives a-b-c. A dict iterates over its keys, so ", ".join({"host": 1, "port": 2}) gives host, port. An empty list returns the empty string "", and a single-item list returns that item with no separator at all.