Skip to content
VLSI Mentor

Python Part I · Module 5 · Lists

Combining and Repeating Lists

You have two lists of tests and want one. You want a list of ten zeros without typing ten zeros. Both are ordinary needs and Python handles them with the same two operators that joined and repeated strings back in Chapter 4.3 — which means the notation costs you nothing to learn. What does need care is the question this module keeps asking: does this change the list I have, or build a new one? Combining lists can be done either way, and the two ways look almost identical on the page. This chapter separates them, finishes the append comparison Chapter 5.3 started, and meets a repetition trap that has caught a very large number of people.

Foundation13 min readPythonPart IFundamentalsListsOperators

Part I · Module 5 · Chapter 5.6 · Combining and Repeating Lists

1. Plus Builds a New List

+ between two lists produces a list containing the elements of the first followed by the elements of the second — exactly as it did for strings in Chapter 4.3:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
a = [1, 2]
b = [3, 4]
c = a + b
print(c)
print(a)
print(b)
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
[1, 2, 3, 4]
[1, 2]
[3, 4]

Three lists now exist. The important lines are the last two: a and b are unchanged. + built something new and left both operands alone, which is the same behaviour every operator in Module 3 had.

That makes + the safe choice when the original lists still matter.

Both operands have to be lists. Python will not combine a list with something else:

Azvya Education Pvt. Ltd.VLSI Mentor
BROKEN ON PURPOSE
print([1, 2] + "ab")
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
TypeError: can only concatenate list (not "str") to list
Azvya Education Pvt. Ltd.VLSI Mentor
BROKEN ON PURPOSE
print([1, 2] + 3)
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
TypeError: can only concatenate list (not "int") to list

Compare that message with the one from Chapter 4.3 — can only concatenate str (not "int") to str. Same sentence, different type. To add a single value to a list, either put it in a list of its own ([1, 2] + [3]) or use append from Chapter 5.3.

2. extend() Changes the List You Have

extend is the mutating alternative. It adds every element of one list onto the end of another, and the list you called it on is what changes:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
a2 = [1, 2]
b2 = [3, 4]
print(a2)
a2.extend(b2)
print(a2)
print(b2)
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
[1, 2]
[1, 2, 3, 4]
[3, 4]

Before and after on a2. And b2 — the list handed to extend — is untouched; only the receiver changes.

Being a mutating method, it behaves like every other one from Chapter 5.3:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
x = [1]
print(x.extend([2]), x)
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
None [1, 2]

None, and the list changed. So a = a.extend(b) sets a to None, which is the same trap in a new place.

3. append() and extend(): The Shape Difference

Chapter 5.3 showed that append adds exactly one element, and noted that the alternative would arrive later. This is it, and the difference is far easier to see than to define:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
p = ["smoke"]
p.append(["reset", "burst"])
print(p, len(p))
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
['smoke', ['reset', 'burst']] 2
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
q = ["smoke"]
q.extend(["reset", "burst"])
print(q, len(q))
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
['smoke', 'reset', 'burst'] 3

Same starting list, same argument, completely different results.

append was handed one value and added one element — and that element is a list, sitting inside the outer one. Two elements total.

extend was handed a list and added each of its elements separately. Three elements total, all strings, nothing nested.

There is one surprise worth knowing, and it is not an error, which makes it worse:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
x = ["a"]
x.extend("bc")
print(x)
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
['a', 'b', 'c']

extend was given a string rather than a list, and it added each of its characters. That follows from the rule above — a string supplies its characters one at a time, exactly as Chapter 4.9 showed when join did the same thing. If you meant to add one element, append("bc") is the call.

4. The Same Choice, Again

You have now met this decision three times, and it is worth naming as a pattern rather than three separate facts:

Build a new valueChange the one you have
a + ba.extend(b)
sorted(a)a.sort()
a[:] (Chapter 5.2)—

Everything in the left column leaves your list alone and hands you something; everything in the right column changes your list and hands you None.

5. Two Lines That Look the Same and Are Not

This is the most valuable thing in the chapter, and it follows directly from Section 4.

Chapter 3.3 taught +=. On a list it mutates:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
m = [1, 2]
n = m
m += [3]
print(m)
print(n)
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
[1, 2, 3]
[1, 2, 3]

Both names show the change, because there is one list and += changed it.

Now the version that looks equivalent:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
m2 = [1, 2]
n2 = m2
m2 = m2 + [3]
print(m2)
print(n2)
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
[1, 2, 3]
[1, 2]

n2 is different. Here m2 + [3] built a new list — Section 1 — and m2 = rebound the name to it. The original list was never changed, and n2 still refers to it.

So m += [3] and m = m + [3] produce the same value for m and do genuinely different things to everything else. The first mutates; the second rebinds.

6. Repeating a List

* with a whole number repeats a list, just as it repeated a string:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
print([0] * 3)
print(["-"] * 4)
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
[0, 0, 0]
['-', '-', '-', '-']

This is the normal way to build a list of a known length filled with a starting value — ten zeros to count into, say, rather than typing them out.

It builds a new list and leaves the original alone:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
z = [1, 2]
w = z * 2
print(z, w)
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
[1, 2] [1, 2, 1, 2]

And the edges behave as Chapter 4.3 established for strings — zero or a negative count gives an empty result, quietly:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
print([0] * 0)
print([0] * -2)
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
[]
[]

Which is worth remembering, because a repetition count that comes out negative gives you an empty list rather than a complaint.

7. The Repetition Trap

Now a warning, because you will try this the first time you need a grid.

Repeating a list whose element is itself a list does something that looks right and is not:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
rows = [[0] * 3] * 2
print(rows)
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
[[0, 0, 0], [0, 0, 0]]

That looks exactly like two rows of three zeros. Now change one value in the first row:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
rows = [[0] * 3] * 2
rows[0][0] = 9
print(rows)
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
[[9, 0, 0], [9, 0, 0]]

Both rows changed. One assignment, two rows different.

The reason is that * 2 did not make two rows. It made an outer list with two elements, and both of those elements are the same inner list — repetition repeats the element you gave it, and the element was one list. Changing it through the first position is changing it through the second position, because there is only one inner list to change.

Written out row by row, it behaves as expected:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
safe = [[0] * 3, [0] * 3]
safe[0][0] = 9
print(safe)
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
[[9, 0, 0], [0, 0, 0]]

Here [0] * 3 was evaluated twice, producing two separate inner lists, so changing one leaves the other alone.

8. Predict, Run, Observe

Predict every line before reading on. Two of these are the traps from Sections 5 and 7.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
a = ["smoke"]
b = ["reset", "burst"]
print(a + b)
print(a)
a.extend(b)
print(a)
print(len(a))
c = ["x"]
c.append(["y", "z"])
print(c, len(c))
d = [1, 2]
e = d
d = d + [3]
print(e)

The verified output:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
['smoke', 'reset', 'burst']
['smoke']
['smoke', 'reset', 'burst']
3
['x', ['y', 'z']] 2
[1, 2]

Line 2 is the point of +: after a + b, a is still just ['smoke']. Line 3 shows extend doing the same combination by changing a instead.

c.append(["y", "z"]) gives a two-element list with a list inside it — Section 3.

And the last line is [1, 2], not [1, 2, 3]. d = d + [3] built a new list and rebound d; e still refers to the original, unchanged. Had that line been d += [3], e would have shown [1, 2, 3]. That one-character difference is Section 5.

9. Exercises

Exercise 1 — combine two ways. Given first = ["smoke", "reset"] and second = ["burst"], write the line that produces a combined list without changing first, and separately the line that adds second's elements into first. Print first after each.

Exercise 2 — predict the shape. Starting from t = ["a"] each time, give the value and len() after each, then verify: (a) t.append("b") (b) t.append(["b", "c"]) (c) t.extend(["b", "c"]) (d) t.extend("bc") (e) t = t + ["b"]

Exercise 3 — find the nested list. A colleague's list of tests prints as ['smoke', ['reset', 'burst']] when they expected three test names. Say which method they used, which they wanted, and how len() would have revealed the problem sooner.

Exercise 4 — the one-character difference. Predict second in each case, then verify and explain the difference in two sentences:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
first = [1, 2]
second = first
first += [3]
print(second)
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
first = [1, 2]
second = first
first = first + [3]
print(second)

Exercise 5 — repetition. Write the expression that builds a list of eight "-" characters, and the one that builds a list of five zeros. Then predict len([0] * 0) and say why it is not an error.

Exercise 6 — the grid trap. Run both of these, write down both results, and explain in three sentences why they differ:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
g1 = [[0] * 2] * 2
g1[0][0] = 9
print(g1)
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
g2 = [[0] * 2, [0] * 2]
g2[0][0] = 9
print(g2)

Exercise 7 — choose the tool. For each, name +, extend or append and justify in one sentence: (a) add one new failure to the failures list (b) merge yesterday's failures into today's list, keeping yesterday's list intact (c) add every name from a second test list into the working list

10. Summary

  • a + b builds a new list and leaves both originals unchanged. Both operands must be lists.
  • a.extend(b) adds b's elements into a, changing a and returning None. b is untouched.
  • append(x) adds x as one element; extend(xs) adds each element of xs. Appending a list nests it; len() is how you notice.
  • extend given a string adds its characters — not an error, and rarely what you meant.
  • The recurring choice: + / sorted() / [:] build something new; extend / sort() change what you have. Ask whether anything still needs the list as it is.
  • a += [x] mutates the list; a = a + [x] rebinds the name. On lists these are not interchangeable, and any other name referring to that list can tell the difference.
  • a * n repeats a list into a new list. A zero or negative count gives [], quietly.
  • [[0] * 3] * 2 does not make two rows. It makes one row referred to twice, so changing one changes both. Write the rows out instead.

Repeating a list of lists went wrong. The next chapter works with lists of lists deliberately.

Where this fits

Part of the Python curriculum.