Joining is how you turn a Python list to a string: ", ".join(my_list). The text before .join is the separator, and the list goes inside the parentheses:
groceries = ["apples", "bread", "milk"]
print(", ".join(groceries)) # apples, bread, milk
That covers a list of strings. Numbers, None values and speed each need a little more, and I’ll show each one with real output from Python 3.14.7.

join() puts the separator between items, never after the last one.Convert a list to a string in Python with join()
Here’s the same list joined three ways, plus what str() does to it:
groceries = ["apples", "bread", "milk"]
print(repr("".join(groceries)))
print(repr(" ".join(groceries)))
print(repr(", ".join(groceries)))
print()
print("str(groceries) gives:", str(groceries))
Output:
'applesbreadmilk'
'apples bread milk'
'apples, bread, milk'
str(groceries) gives: ['apples', 'bread', 'milk']

join on, including an empty one."".join() glues the items together with nothing between them, " ".join() puts a space between them, and ", ".join() gives a readable list.
The last line shows why str() is rarely the answer. It gives you the list exactly as Python prints it, brackets and quotes included.
Can you use str() on a list?
You can, but it answers a different question. str(groceries) returns "['apples', 'bread', 'milk']", a text copy of the list’s code-like form.
That’s useful for logging or debugging, where you want to see the structure. For anything a person will read, like a message or a CSV cell, use join().
If you only need to print a list without the brackets, printing a list without brackets covers the other options.
Join a list of numbers into a string
join() only accepts strings, so a list of numbers fails straight away:
order_ids = [1042, 1043, 1044]
try:
", ".join(order_ids)
except TypeError as err:
print("TypeError:", err)
print(", ".join(map(str, order_ids)))
print(", ".join(str(n) for n in order_ids))
print()
prices = [19.99, 5, 42.5]
print(", ".join(f"${p:.2f}" for p in prices))
Output:
TypeError: sequence item 0: expected str instance, int found
1042, 1043, 1044
1042, 1043, 1044
$19.99, $5.00, $42.50

map(str, ...) converts them all.The message tells you exactly what went wrong: sequence item 0: expected str instance, int found. The index points at the first item that isn’t a string.
Join a list of digits into one number
A related trick: turning a list of digits into a single number. Join them as text, then convert:
digits = [2, 0, 2, 6]
year_text = "".join(map(str, digits))
print(repr(year_text), "->", int(year_text) + 1)
Output:
'2026' -> 2027
"".join(map(str, digits)) builds the text "2026", and int() turns it into a number you can do math with.
map(str, order_ids) converts every item on the way in, and a generator expression does the same. I use the f-string version when the numbers need formatting, like the dollar amounts in the last line.
Join with a separator: comma, space, newline or none
The separator can be any string at all, which covers most of the formats people ask for:
states = ["Texas", "Ohio", "Utah"]
print(repr(", ".join(states))) # comma and space
print(repr(",".join(states))) # comma only, CSV-style
print(repr(" | ".join(states))) # any text works
print(repr("".join(states))) # no separator at all
print("\n".join(states)) # one per line
Output:
'Texas, Ohio, Utah'
'Texas,Ohio,Utah'
'Texas | Ohio | Utah'
'TexasOhioUtah'
Texas
Ohio
Utah
A comma with no space gives CSV-style output, and an empty string removes the separator completely. "\n".join() puts each item on its own line.
There’s more on newlines in the Python newline guide.
Join lines and write them to a file
"\n".join() is the quick way to write a list of lines to a file. Just remember the last line:
lines = ["order 1", "order 2", "order 3"]
with open("orders.txt", "w") as f:
f.write("\n".join(lines) + "\n") # join adds no newline after the last line
print(repr(open("orders.txt").read()))
Output:
'order 1\norder 2\norder 3\n'
join() only puts the newline between items, so I add one more at the end. Many tools expect a text file to finish with a newline, and appending to it later then starts on a fresh line.
Put quotes around each item
Sometimes each item needs quotes, for a message or a config value. Wrap each one on the way into join():
states = ["Texas", "Ohio", "Utah"]
print(", ".join(f'"{s}"' for s in states))
print(", ".join(repr(s) for s in states))
Output:
"Texas", "Ohio", "Utah"
'Texas', 'Ohio', 'Utah'
The f-string version always uses double quotes, while repr() uses Python’s own quoting. If you’re tempted to build a SQL IN (...) list this way, don’t; pass the values as query parameters instead, so quotes inside the data can’t break the query.
Split the string back into a list
split() is the reverse of join(), as long as you use the same separator:
states = ["Texas", "Ohio", "Utah"]
text = ",".join(states)
back = text.split(",")
print(repr(text))
print(back)
print("same list again?", back == states)
Output:
'Texas,Ohio,Utah'
['Texas', 'Ohio', 'Utah']
same list again? True
The round trip only works if no item contains the separator itself. For the details of the reverse direction, see splitting a string by comma.
Join a list of characters into a word
A list of single characters joins into a word with an empty separator:
letters = ["P", "y", "t", "h", "o", "n"]
word = "".join(letters)
print(repr(word))
print(list(word) == letters, "- list() splits it back into characters")
Output:
'Python'
True - list() splits it back into characters
list(word) goes the other way, splitting a string into its characters. That pair is common in puzzles and in code that edits one character at a time.
Join only the first few items
For previews, show a few items and summarize the rest. Slicing does the work before join():
states = ["Texas", "Ohio", "Utah", "Iowa", "Maine", "Idaho"]
shown = 3
preview = ", ".join(states[:shown])
extra = len(states) - shown
print(preview + (f" and {extra} more" if extra > 0 else ""))
Output:
Texas, Ohio, Utah and 3 more
states[:shown] takes the first three, and the count of the rest turns into “and 3 more”. It keeps a long list from flooding a message or a table cell.
Why is there no list.join() in Python?
If you’re coming from JavaScript, where it’s array.join(", "), this trips you up at first:
fruits = ["apple", "pear"]
try:
fruits.join(", ") # the JavaScript habit
except AttributeError as err:
print("AttributeError:", err)
print(", ".join(fruits)) # separator first, list inside
Output:
AttributeError: 'list' object has no attribute 'join'
apple, pear
In Python, join belongs to the separator string, not the list. It reads backwards at first, but it lets join accept any iterable, not just lists, as the next sections show.
Java’s String.join(", ", list) is actually the closest match: separator first, then the items.
Join a list with None or mixed types
Real data often mixes strings with numbers and missing values. Here’s what happens:
values = ["Austin", None, "Denver", 2026]
try:
", ".join(values)
except TypeError as err:
print("TypeError:", err)
print("map(str): ", ", ".join(map(str, values)))
print("skip None: ", ", ".join(str(v) for v in values if v is not None))
Output:
TypeError: sequence item 1: expected str instance, NoneType found
map(str): Austin, None, Denver, 2026
skip None: Austin, Denver, 2026

map(str, ...) turns None