How to concatenate two lists in Python

There are five reasonable ways to join two lists. They differ in whether you get a new list or modify an existing one, and that difference is visible to other code holding the same list.

The options

a = [1, 2]
b = [3, 4]
a + b             : [1, 2, 3, 4]     new list
[*a, *b]          : [1, 2, 3, 4]     new list
list(chain(a, b)) : [1, 2, 3, 4]     new list
x.extend(b)       : [1, 2, 3, 4]     modifies x
y += b            : [1, 2, 3, 4]     modifies y

The first three leave both inputs alone. The last two modify the left-hand list.

Which one you want

a + b when you want a third list and the originals should stay as they are. The clearest option, and the default choice.

[*a, *b] (PEP 448) when you are joining more than two things, or mixing types. It takes any iterables, not just lists, and reads well with three or four of them.

a.extend(b) when you are appending to a list you own and do not need a copy. Cheapest, since nothing is rebuilt.

chain(a, b) when you only want to iterate. It builds nothing at all — useful when the lists are large and the result feeds straight into a loop.

+ and += are not the same thing

This is the part worth knowing. Verified with id():

p = p + [3]   : new object? True
q += [3]      : new object? False   <- same object, mutated
r.extend([3]) : new object? False

+= on a list is not shorthand for x = x + y. It calls __iadd__, which extends in place and hands back the same object.

Which matters as soon as two names refer to the same list:

after m += [3]      : m=[1, 2, 3] alias=[1, 2, 3]   <- alias saw it
after m2 = m2 + [3] : m2=[1, 2, 3] alias2=[1, 2]    <- alias did NOT

Two lines that look equivalent, and the second name — a caller’s variable, an entry in a dict, an attribute on an object — sees one and not the other. If that list came in as a function parameter, += changes it for the caller.

append is not extend

The other thing people mix up:

[1,2].append([3,4]) : [1, 2, [3, 4]]   <- one new element, a list
[1,2].extend([3,4]) : [1, 2, 3, 4]

append adds one item, whatever it is. extend adds each item of an iterable.

extend is permissive, + is strict

[1,2] + (3,4) -> TypeError: can only concatenate list (not "tuple") to list

+ insists both sides are lists. extend takes anything iterable:

extend with tuple, str, generator : [1, 2, 3, 4, 'a', 'b', 5]

Look at what happened to the string. extend("ab") added 'a' and 'b' as two separate elements, because a string is an iterable of characters. If you meant to add one string, that is append. This is the same class of surprise as the recursive flatten in the article on flattening lists.

[*a, *b] also takes any iterables, without the string problem being hidden — you can see the unpacking.

Do not use + in a loop

out = []
for part in parts:
    out = out + part      # rebuilds the whole list every time
2000 sublists, 3 runs each:
  out = out + p       : 0.0071 s
  out.extend(p)       : 0.0001 s
  chain.from_iterable : 0.0001 s
  '+' is 68x extend

Each + allocates a new list and copies everything accumulated so far, so the loop is quadratic. extend appends into the existing one.

For joining a list of lists, chain.from_iterable is the direct expression of what you want, and the article on flattening covers it in more detail.

The repetition trap

Related, and worth a mention because it comes up in the same threads:

[[0]] * 3 : [[0], [0], [0]]
after rows[0].append(9) : [[0, 9], [0, 9], [0, 9]]

* on a list repeats the reference, so all three rows are the same list. Appending through one changes all of them.

[[0] for _ in range(3)] : [[0, 9], [0], [0]]

The comprehension evaluates [0] each time round, giving three separate lists. For a grid or a list of buckets, that is what you want.

One more difference

Concatenation keeps order and duplicates, which sets do not:

[1,2] + [2,1]            : [1, 2, 2, 1]
set union for comparison : [1, 2]

If you find yourself reaching for set(a) | set(b) to join two lists, be sure you actually want the deduplication and do not mind losing the order.

About Netcup (advertisement)

The German host Netcup offers, among other things, affordable and powerful web hosting packages, KVM-based root servers and dedicated servers. With our voucher codes you can save even more (6€ off your first order, 30% off all KVM-based root servers, ...).