Report this

What is the reason for this report?

Join Python List: Methods, Examples, and Best Practices

Updated on June 16, 2026
Join Python List: Methods, Examples, and Best Practices

Introduction

In Python, joining a list means combining its elements into a single string, using a separator to place between each element. The standard tool for this is the string join() method, which takes an iterable (such as a list) and returns one string. This operation is useful whenever you need to turn a list of values into text, for example saving a list of names as a comma-separated line in a file or building a sentence from a list of words.

In this tutorial, you will learn the different ways to join a Python list and combine lists in Python. You will start with the join() method for joining a list of strings, then cover how to join a list of integers and mixed data types, how to handle None values and nested lists, how to concatenate two or more lists with the + operator, list.extend(), the * unpacking operator, and itertools.chain(), and how the methods compare in performance. You will also see why join() lives on the string class rather than the list class, and which modern alternatives Python now offers.

Key Takeaways:

  • The string join() method is the standard and fastest way to join a list of strings into one string with a chosen separator.
  • The syntax is separator.join(iterable): the separator is the string you call join() on, and the list is passed as the argument.
  • join() works only on strings, so joining a list that contains integers, None, or other types raises a TypeError.
  • To join a list of integers, convert each item to a string first with map(str, list) or a list comprehension.
  • To skip None values before joining, use a generator expression such as ",".join(x for x in items if x is not None).
  • Use the + operator or list.extend() to concatenate two lists into a new list, and the * unpacking operator (Python 3.5+) for a concise merge.
  • For joining many or very large lists, itertools.chain() avoids building intermediate lists and uses less memory.
  • Building a string with repeated + in a loop runs in O(n^2) time, while join() runs in O(n); in a benchmark on 100,000 items, join() was about 8 times faster.
  • join() is a string method (not a list method) because it works with any iterable and always returns a string.

Join a Python list of strings with join()

We can use the Python string join() method to join a list of strings. The method is called on the separator string and takes the iterable as its argument, so the general form is separator.join(iterable). Because a list is an iterable, we can pass a list directly. The list should contain only strings; if you try to join a list of integers you will get a TypeError, which the note below explains how to fix.

Let’s look at a short example that joins a list of vowels into a comma-separated string:

vowels = ["a", "e", "i", "o", "u"]
vowels_csv = ",".join(vowels)
print("Vowels are =", vowels_csv)
Output
Vowels are = a,e,i,o,u

The separator can be any string, not just a comma. The next section shows how to join with a space, a newline, or any custom separator.

Join a list with a comma, space, newline, or custom separator

Because the separator is just the string you call join() on, you can change the output format simply by changing that string. A space joins words into a sentence, "\n" puts each item on its own line, and any other string works as a custom separator.

The following example joins the same list three different ways:

words = ["Python", "is", "awesome"]

print(" ".join(words))      # space separator
print("\n".join(words))     # newline separator
print(" -> ".join(words))   # custom separator
Output
Python is awesome
Python
is
awesome
Python -> is -> awesome

The newline join is especially handy when writing a list to a text file, where each element should appear on its own line.

Join an empty list

Joining an empty list is safe and simply returns an empty string, so you do not need a special check for it.

print(repr("".join([])))
print(repr(",".join([])))
Output
''
''

This predictable behavior is convenient when the list might be empty at runtime, since the call will not raise an error.

Join a list of integers in Python

A list of integers cannot be passed to join() directly, because join() only accepts strings. Attempting it raises TypeError: sequence item 0: expected str instance, int found. The fix is to convert each element to a string first, and there are two common ways to do that.

Using map() with join()

The map() function applies str to every element of the list, producing string versions that join() can accept. This is concise and reads well for simple conversions, and you can learn more about it in the tutorial on the Python map() function.

int_list = [1, 2, 3, 4, 5]
joined_str = ",".join(map(str, int_list))
print("Joined string:", joined_str)
Output
Joined string: 1,2,3,4,5

Using a list comprehension for type conversion

A list comprehension does the same conversion and is a good choice when you want to transform each item further, for example formatting numbers while converting them.

int_list = [1, 2, 3, 4, 5]
joined_str = ",".join([str(element) for element in int_list])
print("Joined string:", joined_str)
Output
Joined string: 1,2,3,4,5

Both approaches are correct; map(str, ...) is slightly faster and more compact, while the comprehension is easier to extend when each element needs extra formatting.

Join a list with mixed data types

When a list mixes strings with other types, join() raises a TypeError because it expects every element to already be a string. The list ['Java', 'Python', 1] is a typical example: the integer 1 causes the error.

names = ['Java', 'Python', 1]
single_str = ','.join(names)
print(single_str)

This raises the following error:

Output
TypeError: sequence item 2: expected str instance, int found

The lesson is that a list containing multiple data types cannot be combined into a single string with join() directly; you must convert the non-string items first, as the next two sections show.

Handling None values in a list before joining

A None value triggers the same TypeError, so you need to decide whether to drop the None values or convert them to text. To drop them, filter the list with a generator expression before joining.

items = ["a", None, "b"]
result = ",".join(x for x in items if x is not None)
print(result)
Output
a,b

If you would rather keep a placeholder for the missing value, convert every element with str() instead, which turns None into the text "None".

items = ["a", None, "b"]
result = ",".join(str(x) for x in items)
print(result)
Output
a,None,b

Choose the filtering approach when missing values should disappear, and the conversion approach when their position matters.

Filtering non-string items before joining

The same generator-expression pattern lets you keep only the items you want, for example joining just the string elements of a mixed list and ignoring everything else.

mixed = ['Java', 'Python', 1, None, 'Go']
result = ",".join(x for x in mixed if isinstance(x, str))
print(result)
Output
Java,Python,Go

Using isinstance(x, str) keeps the strings and silently skips the integer and the None, which is useful when a list may contain unpredictable types.

Join a nested list in Python

A nested list (a list of lists) cannot be joined directly either, because its elements are lists rather than strings. You first need to flatten it into a single sequence of strings, and there are two clean ways to do that.

Flattening a nested list with a list comprehension

A nested list comprehension walks each sublist and then each item, producing a flat list of strings that join() can accept.

nested = [["a", "b"], ["c", "d"]]
flat = [item for sub in nested for item in sub]
print(",".join(flat))
Output
a,b,c,d

Using itertools.chain.from_iterable()

For larger nested lists, itertools.chain.from_iterable() flattens the structure lazily without building an intermediate list, which is more memory efficient.

import itertools

nested = [["a", "b"], ["c", "d"]]
print(",".join(itertools.chain.from_iterable(nested)))
Output
a,b,c,d

Both produce the same result; reach for chain.from_iterable() when the nested data is large or streamed, and the comprehension when readability matters most.

What happens when you pass a string to join()

A common point of confusion is calling join() on a single string instead of a list. Because a string is itself an iterable of characters, join() iterates over its individual characters and inserts the separator between each one.

message = "Hello ".join("World")
print(message)
Output
WHello oHello rHello lHello d

As the output shows, this does not produce "Hello World". Because 'World' is a string and strings are iterables of characters, join() inserts 'Hello ' between every individual character of 'World', not between two whole words. To combine two whole strings, use concatenation with + or an f-string instead of join().

Why join() is a string method and not a list method

A question many Python developers ask is why join() belongs to the string class and not to the list class. Wouldn’t the syntax below be easier to remember?

vowels_csv = vowels.join(",")

There is a well-known StackOverflow discussion about this. The core reasoning is that join() works with any iterable, not just lists, and it always returns a string. If join() were a method on every iterable, the same logic would have to be duplicated across lists, tuples, sets, generators, and more. Placing it on the string class instead means a single implementation handles every iterable, and the separator is naturally the string you call the method on.

The official Python documentation describes str.join() as follows: “Return a string which is the concatenation of the strings in iterable.” It also notes that “a TypeError will be raised if there are any non-string values in iterable, including bytes objects.” This is exactly the behavior you saw in the integer and mixed-type examples above, and it confirms why the method is defined where it is.

Concatenate two or more lists in Python

Joining for string output is different from concatenating lists into a new list. When your goal is a combined list rather than a string, Python gives you several options, each with different performance characteristics. You can read more in the tutorial on concatenating lists in Python.

Using the + operator

The + operator creates a new list by joining two existing lists, which is the simplest approach for a simple merge of small lists.

list1 = [1, 2, 3]
list2 = [4, 5, 6]
combined = list1 + list2
print(combined)
Output
[1, 2, 3, 4, 5, 6]

Using list.extend()

The list.extend() method adds every element of one list onto another in place, without creating a new list. It is the right choice when you are growing an existing list, and it is covered alongside other list-growing methods in the tutorial on adding elements to a list in Python.

combined = [1, 2, 3]
combined.extend([4, 5, 6])
print(combined)
Output
[1, 2, 3, 4, 5, 6]

Using the * unpacking operator (Python 3.5+)

Since Python 3.5, the * unpacking operator can be used inside list literals to spread several lists into a new list. It is concise and works with more than two lists at once.

list1 = [1, 2, 3]
list2 = [4, 5, 6]
combined = [*list1, *list2]
print(combined)
Output
[1, 2, 3, 4, 5, 6]

Using itertools.chain() for memory-efficient joins

For merging many lists, or very large ones, itertools.chain() returns an iterator that yields elements from each list in turn without building intermediate lists, which keeps memory usage low.

import itertools

list1 = [1, 2, 3]
list2 = [4, 5, 6]
combined = list(itertools.chain(list1, list2))
print(combined)
Output
[1, 2, 3, 4, 5, 6]

Joining two lists without duplicates

If you need a combined list with duplicate values removed while preserving order, concatenate the lists and pass the result to dict.fromkeys(), which keeps only the first occurrence of each value.

list1 = [1, 2, 3]
list2 = [3, 4, 5]
combined = list(dict.fromkeys(list1 + list2))
print(combined)
Output
[1, 2, 3, 4, 5]

Using dict.fromkeys() preserves insertion order (guaranteed since Python 3.7), which a plain set() would not.

Split a string back into a list with split()

The split() method is the inverse of joining: it breaks a string into a list using a delimiter. This is useful when you have joined a list into a string and later need the original list back.

names = ['Java', 'Python', 'Go']
delimiter = ','
single_str = delimiter.join(names)
print('String:', single_str)

split = single_str.split(delimiter)
print('List:', split)
Output
String: Java,Python,Go
List: ['Java', 'Python', 'Go']

Using the same delimiter to split the string returns the original list, so join() and split() round-trip cleanly.

Splitting only n times

The split() method takes an optional second argument that limits how many splits are performed, which is handy when only the first few fields matter.

names = ['Java', 'Python', 'Go']
delimiter = ','
single_str = delimiter.join(names)

split = single_str.split(delimiter, 1)
print('List:', split)
Output
List: ['Java', 'Python,Go']

Because the split count was set to 1, the operation ran only once and left the remaining delimiter in place.

Performance comparison: which join method is fastest?

When combining strings or lists, the method you choose has real performance implications, especially as the data grows. The key idea is time complexity: join() and extend() run in linear O(n) time, while repeatedly using + in a loop runs in quadratic O(n^2) time because each + creates a brand-new object and copies all the existing elements again.

Benchmark results for small vs. large lists

To make this concrete, the following benchmark builds a string of 100,000 items two ways, using join() and using += in a loop, and times each with the timeit module.

import timeit

words = [str(i) for i in range(100_000)]

def with_join():
    return ",".join(words)

def with_plus_loop():
    s = ""
    for w in words:
        s += w + ","
    return s

print(f"join : {timeit.timeit(with_join, number=20) / 20 * 1000:.2f} ms")
print(f"+=   : {timeit.timeit(with_plus_loop, number=20) / 20 * 1000:.2f} ms")

On one test machine running Python 3.14, this produced the following output (your exact numbers will vary by hardware):

Output
join : 1.48 ms
+=   : 12.61 ms

In this run, join() was about 8 times faster than building the string with +=. The gap widens as the list grows, which is the practical consequence of the O(n) versus O(n^2) difference. The same pattern holds for lists: concatenating 1,000 lists of 1,000 integers each with repeated out = out + lst took roughly 540 ms in our testing, while list.extend() took about 1 ms and itertools.chain() about 3 ms, since the repeated + rebuilds and copies the growing list on every step.

Time and space complexity by method

The table below summarizes the complexity and memory behavior of each method so you can choose based on the size and shape of your data.

Method Time Complexity Creates Intermediate Copies? Best For
str.join() O(n) No (single pass) Joining a list of strings into one string
+ in a loop O(n²) Yes (new object each step) Avoid for large data; fine for a single small merge
list.extend() O(n) No (in place) Growing an existing list
itertools.chain() O(n) No (lazy iterator) Merging many or very large lists

Choose the method that fits your use case: join() for strings, extend() or chain() for lists, and reserve a single + for small, one-off merges.

Method comparison table

Beyond raw performance, the methods differ in what they are for, how readable they are, and which Python versions support them. The table below compares the main options at a glance.

Method Use case Output Readability Python version
str.join() Join a list of strings into one string str High All 3.x
+ operator Concatenate two lists or two strings new list / str High All 3.x
list.extend() Add items to an existing list in place modifies list High All 3.x
* unpacking Merge several lists into a new list new list Medium 3.5+
itertools.chain() Iterate over many lists without copying iterator Medium All 3.x
dict | operator Merge two dictionaries new dict High 3.9+

Modern alternatives: unpacking and the | operator

Recent Python versions added cleaner syntax for merging collections. The * unpacking operator, shown earlier, is the idiomatic modern way to merge lists into a new list. For dictionaries, PEP 584 introduced the | merge operator in Python 3.9, giving dictionaries the same kind of concise merge syntax that + gives lists.

defaults = {"theme": "light", "lang": "en"}
overrides = {"lang": "fr"}
settings = defaults | overrides
print(settings)
Output
{'theme': 'light', 'lang': 'fr'}

When the same key appears in both dictionaries, the value from the right-hand dictionary wins, which is why lang becomes "fr". There is also an in-place version, |=, which updates the left dictionary instead of creating a new one.

Some practical use cases

These methods map naturally onto everyday tasks, so it helps to see when each one shines.

  • The first common case is using join() for strings: when you have a list of strings and need a single delimited string, join() is the preferred method. This is useful for building a sentence from a list of words or combining names with commas, as shown throughout this tutorial.

  • The second common case is combining lists with itertools.chain(): for merging multiple lists, especially large ones, itertools.chain() offers a memory-efficient solution because it avoids creating intermediate lists. This matters when working with large datasets or when you need to process elements from several lists in a single iteration. You can read more in the tutorial on concatenating lists in Python.

Combining lists with dictionaries or sets

Lists, dictionaries, and sets behave differently, so combining them requires a little care. Lists are ordered sequences, dictionaries are collections of key-value pairs (ordered by insertion since Python 3.7), and sets are unordered collections of unique elements. The examples below show how to merge a list with each of the other two.

To combine a list with a dictionary, extract the dictionary’s keys or values and concatenate them with the list. This is useful when merging data from different sources.

my_list = [1, 2, 3]
my_dict = {'a': 4, 'b': 5}

combined_keys = my_list + list(my_dict.keys())
print(combined_keys)

combined_values = my_list + list(my_dict.values())
print(combined_values)
Output
[1, 2, 3, 'a', 'b']
[1, 2, 3, 4, 5]

In the code above, list() converts the dictionary’s keys or values into a list, which is then concatenated with my_list using the + operator.

To combine a list with a set, convert the set to a list first, since sets are unordered and cannot be concatenated to a list directly.

my_list = [1, 2, 3]
my_set = {4, 5, 6}

combined = my_list + list(my_set)
print(combined)
Output
[1, 2, 3, 4, 5, 6]

Here list() turns the set into a list before concatenation. Note that the order of elements coming from the set is not guaranteed, because sets are unordered.

FAQs

1. How to join a list of strings in Python?

To join a list of strings into a single string, call the join() method on a separator and pass the list as the argument, using the form separator.join(list). The separator can be any string, such as a comma or a space.

words = ['Hello', 'World']
sentence = ' '.join(words)
print(sentence)

This prints Hello World. If the list contains non-string items, convert them first with map(str, list).

2. How can I join two lists in Python?

To combine two lists into one new list, use the + operator for a quick merge, or itertools.chain() for better performance with large lists. Use list.extend() if you want to grow an existing list in place rather than create a new one.

list1 = [1, 2]
list2 = [3, 4]
combined = list1 + list2
print(combined)

This prints [1, 2, 3, 4].

3. What does join() do in Python?

The join() method concatenates the strings in an iterable into a single string, separated by the string you call it on. It works on any iterable of strings (lists, tuples, generators) and always returns a string, which is why it is a method of str rather than list.

4. What does strip() do in Python?

The strip() method removes leading and trailing whitespace (or other specified characters) from a string and returns the cleaned string. It is often used to tidy up individual items before or after joining them, for example removing stray spaces parsed from a file.

5. What is the correct syntax for str.join() in Python?

The correct syntax is separator.join(iterable), where the separator is the string placed between elements and iterable is the list (or other iterable) of strings to join. A frequent mistake is writing list.join(separator), which does not exist and raises an AttributeError.

print("-".join(["2026", "06", "09"]))

This prints 2026-06-09, showing the separator placed between each element.

6. How do you join a Python list of integers into a single string?

Convert each integer to a string first, because join() accepts only strings. The simplest way is ",".join(map(str, numbers)), which applies str to every element before joining.

numbers = [1, 2, 3]
print(",".join(map(str, numbers)))

This prints 1,2,3. Joining the integers directly would raise TypeError: sequence item 0: expected str instance, int found.

7. How do you join two separate lists into one list in Python?

To produce a combined list (not a string), use the + operator, the * unpacking operator, list.extend(), or itertools.chain(). This is different from join(), which always returns a string rather than a list.

a = [1, 2]
b = [3, 4]
print([*a, *b])

This prints [1, 2, 3, 4].

8. What is the difference between join() and concatenation using + in Python?

The join() method is built for combining many strings efficiently in a single pass (O(n)), while + creates a new object every time it runs. Using + once on two small values is fine, but using + repeatedly in a loop is O(n^2) and slow, so join() is preferred for combining a list of strings.

9. How do you join a list with a newline character in Python?

Use "\n" as the separator, so each list element appears on its own line. This is the standard way to prepare a list for writing to a text file.

lines = ["first", "second", "third"]
print("\n".join(lines))

This prints each word on a separate line.

10. Can you join a list that contains None values?

Not directly, because a None value raises TypeError: sequence item N: expected str instance, NoneType found. Filter the None values out with a generator expression, or convert every element with str() if you want a placeholder.

items = ["a", None, "b"]
print(",".join(x for x in items if x is not None))

This prints a,b.

11. When should you use itertools.chain() instead of the + operator?

Use itertools.chain() when merging many lists or very large ones, because it returns a lazy iterator and avoids building intermediate lists, keeping memory usage low. The + operator is fine for a single merge of small lists, but chaining many + operations copies the growing result repeatedly and becomes slow.

12. Why does Python use str.join(list) instead of list.join(str)?

Because join() works with any iterable and always returns a string, it makes sense to define it once on the string class rather than duplicate it across every iterable type. The separator is naturally the string you call the method on, which is why the design is separator.join(iterable).

Conclusion

In this tutorial, you learned the various ways to join and combine lists in Python. You joined a list of strings with the join() method and customized the separator, handled lists of integers, mixed types, None values, and nested lists, and concatenated lists using the + operator, list.extend(), the * unpacking operator, and itertools.chain(). You also compared the methods by performance and complexity, saw why join() is a string method, and explored modern alternatives like the dictionary | operator in Python 3.9+.

To further enhance your knowledge of Python programming, we recommend checking out the following tutorials:

Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.

Learn more about our products

About the author(s)

Pankaj Kumar
Pankaj Kumar
Author
See author profile

Java and Python Developer for 20+ years, Open Source Enthusiast, Founder of https://www.askpython.com/, https://www.linuxfordevices.com/, and JournalDev.com (acquired by DigitalOcean). Passionate about writing technical articles and sharing knowledge with others. Love Java, Python, Unix and related technologies. Follow my X @PankajWebDev

Anish Singh Walia
Anish Singh Walia
Editor
Sr Technical Content Strategist and Team Lead
See author profile

I help Businesses scale with AI x SEO x (authentic) Content that revives traffic and keeps leads flowing | 3,000,000+ Average monthly readers on Medium | Sr Technical Writer(Team Lead) @ DigitalOcean | Ex-Cloud Consultant @ AMEX | Ex-Site Reliability Engineer(DevOps)@Nutanix

Manikandan Kurup
Manikandan Kurup
Editor
Senior Technical Content Engineer I
See author profile

With over 6 years of experience in tech publishing, Mani has edited and published more than 75 books covering a wide range of data science topics. Known for his strong attention to detail and technical knowledge, Mani specializes in creating clear, concise, and easy-to-understand content tailored for developers.

Category:
Tags:

Still looking for an answer?

Was this helpful?

message = "Hello ".join(“World”) print(message) #prints ‘Hello World’ This actually prints: ‘WHello oHello rHello lHello d’

- Miguel

Creative CommonsThis work is licensed under a Creative Commons Attribution-NonCommercial- ShareAlike 4.0 International License.
Join the Tech Talk
Success! Thank you! Please check your email for further details.

Please complete your information!

The developer cloud

Scale up as you grow — whether you're running one virtual machine or ten thousand.

Start building today

From GPU-powered inference and Kubernetes to managed databases and storage, get everything you need to build, scale, and deploy intelligent applications.

Dark mode is coming soon.