Python
Lists
Concatenation
Programming
Python Tips

How can I get the concatenation of two lists in Python without modifying either one?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Concatenating two lists in Python without modifying either original list means creating a new list that contains elements from both. The simplest approach is the + operator (list1 + list2), which always returns a new list. Other options include unpacking with [*list1, *list2], itertools.chain, and list comprehensions. Each has different performance characteristics depending on whether you need a concrete list or just need to iterate over the combined elements.

Method 1: The + Operator

python
1a = [1, 2, 3]
2b = [4, 5, 6]
3
4c = a + b
5print(c)  # [1, 2, 3, 4, 5, 6]
6
7# Originals are unchanged
8print(a)  # [1, 2, 3]
9print(b)  # [4, 5, 6]

The + operator creates a new list by copying all elements from both lists. Neither a nor b is modified. This is the most readable and commonly used approach.

Method 2: Unpacking with *

python
1a = [1, 2, 3]
2b = [4, 5, 6]
3
4c = [*a, *b]
5print(c)  # [1, 2, 3, 4, 5, 6]

The * unpacking syntax (Python 3.5+) expands each list inside a new list literal. This also works with more than two lists and with other iterables:

python
1a = [1, 2]
2b = (3, 4)       # tuple
3c = {5, 6}       # set
4d = range(7, 9)  # range
5
6combined = [*a, *b, *c, *d]
7print(combined)  # [1, 2, 3, 4, 5, 6, 7, 8]
8# (set order may vary)

Method 3: itertools.chain (Lazy)

python
1from itertools import chain
2
3a = [1, 2, 3]
4b = [4, 5, 6]
5
6# chain returns an iterator, not a list
7combined = chain(a, b)
8
9for item in combined:
10    print(item)
11# 1 2 3 4 5 6
12
13# Convert to list if needed
14combined_list = list(chain(a, b))
15print(combined_list)  # [1, 2, 3, 4, 5, 6]

chain does not create an intermediate list. It yields elements from the first iterable, then from the second. This is memory-efficient when you only need to iterate once over very large lists.

Method 4: List Comprehension

python
1a = [1, 2, 3]
2b = [4, 5, 6]
3
4c = [x for x in a] + [x for x in b]
5# Or more concisely:
6c = [x for lst in (a, b) for x in lst]
7print(c)  # [1, 2, 3, 4, 5, 6]

This is more verbose than + for simple concatenation but useful when you need to transform or filter elements during concatenation:

python
1a = [1, 2, 3]
2b = [4, 5, 6]
3
4# Concatenate and double each element
5c = [x * 2 for lst in (a, b) for x in lst]
6print(c)  # [2, 4, 6, 8, 10, 12]

Method 5: copy + extend

python
1a = [1, 2, 3]
2b = [4, 5, 6]
3
4c = a.copy()  # or c = a[:]
5c.extend(b)
6print(c)  # [1, 2, 3, 4, 5, 6]
7
8# a is NOT modified because we copied first
9print(a)  # [1, 2, 3]

This is equivalent to a + b but expressed as two steps. It is slightly less readable but useful when building up a list incrementally.

Methods That DO Modify the Original

Be careful with these — they change the first list:

python
1a = [1, 2, 3]
2b = [4, 5, 6]
3
4# extend modifies a in place
5a.extend(b)
6print(a)  # [1, 2, 3, 4, 5, 6] — a is modified!
7
8# += also modifies in place
9a = [1, 2, 3]
10a += b
11print(a)  # [1, 2, 3, 4, 5, 6] — a is modified!

If you want a new list without modifying either original, avoid extend() and += on the original variables.

Performance Comparison

python
1import timeit
2
3a = list(range(10000))
4b = list(range(10000))
5
6# + operator
7timeit.timeit(lambda: a + b, number=10000)
8# ~0.15s
9
10# Unpacking [*a, *b]
11timeit.timeit(lambda: [*a, *b], number=10000)
12# ~0.16s
13
14# list(chain(a, b))
15from itertools import chain
16timeit.timeit(lambda: list(chain(a, b)), number=10000)
17# ~0.20s
18
19# Comprehension
20timeit.timeit(lambda: [x for lst in (a, b) for x in lst], number=10000)
21# ~0.35s

The + operator and [*a, *b] are the fastest for creating a concrete list. chain is fastest when you only need to iterate (no list creation). Comprehensions are slowest due to Python-level loop overhead.

Concatenating Multiple Lists

python
1lists = [[1, 2], [3, 4], [5, 6], [7, 8]]
2
3# Unpacking
4result = [x for lst in lists for x in lst]
5# [1, 2, 3, 4, 5, 6, 7, 8]
6
7# itertools.chain.from_iterable
8from itertools import chain
9result = list(chain.from_iterable(lists))
10# [1, 2, 3, 4, 5, 6, 7, 8]
11
12# sum (works but slow for large lists — O(n^2))
13result = sum(lists, [])
14# [1, 2, 3, 4, 5, 6, 7, 8]

Avoid sum(lists, []) for large lists. It creates a new list at each step, making it O(n^2). Use chain.from_iterable instead.

Common Pitfalls

  • Using extend() or += when you want a new list: Both modify the first list in place. Use + or [*a, *b] to create a new list without side effects.
  • Using append() instead of extend() or +: a.append(b) adds b as a single nested element [1, 2, 3, [4, 5, 6]], not as individual elements.
  • Using sum(lists, []) for many lists: This is O(n^2) because each + creates a new intermediate list. Use itertools.chain.from_iterable for O(n) concatenation of many lists.
  • Assuming shallow copy is deep copy: a + b creates a new list, but the elements themselves are not copied. If elements are mutable (like nested lists), modifying them affects both the original and the concatenated list.
  • Forgetting that chain returns an iterator: chain(a, b) can only be iterated once. If you need to use the result multiple times, convert to a list with list(chain(a, b)).

Summary

  • Use a + b for the simplest, most readable concatenation that returns a new list
  • Use [*a, *b] for concatenating multiple iterables of different types
  • Use itertools.chain(a, b) for memory-efficient lazy iteration without creating a new list
  • Avoid extend() and += when you need to preserve the original lists
  • For concatenating many lists, use chain.from_iterable instead of sum(lists, [])

Course illustration
Course illustration

All Rights Reserved.