Topic 05

Two Names, One List

Hands-on

Back in Chapter 2 you learned to picture names as labels tied to values, not as boxes that hold them. Here is why that picture matters. When two names point at the same list, changing the list through one name changes what the other name sees. It surprises everyone exactly once, and then never again.

Think of two flatmates sharing one shopping list stuck to the fridge. If either adds milk, milk is on the list for both, because there is only one list. If each had a photocopy, their lists would go their separate ways. Python's = never makes a photocopy.

The Surprise

The program below makes a list of workdays, then a second name, week, with week = workdays. It adds Saturday through week. When workdays is printed, Saturday is in it too.

surprise.py
workdays = ["Mon", "Tue", "Wed", "Thu", "Fri"]
week = workdays
week.append("Sat")
print(workdays)
Output
['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']

Why

week = workdays did not create a new list. It tied a second label to the same list. There was only ever one list, with two names on it, so an append through either name changes the one list both names see. The is operator confirms it: week is workdays answers True, meaning they are one and the same object, not just equal-looking.

Two names, one object
>>> workdays = ["Mon", "Tue"]
>>> week = workdays
>>> week is workdays
True

Making a Real Copy

When you want two independent lists, make a copy. The copy method, or list around the original, builds a new list with the same items. Now the two names point at two different lists, and a change to one leaves the other alone.

copy_list.py
workdays = ["Mon", "Tue", "Wed", "Thu", "Fri"]
week = workdays.copy()
week.append("Sat")
print(workdays)
print(week)
Output
['Mon', 'Tue', 'Wed', 'Thu', 'Fri']
['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']

Where It Bites: Functions

Passing a list to a function hands the function a label to the same list, not a copy. So a function that changes a list it was given changes the caller's list. Sometimes that is exactly what you want. When it is not, have the function work on a copy, or build and return a new list instead.

shared.py
def add_weekend(days):
    days.append("Sat")
    days.append("Sun")

workdays = ["Mon", "Tue", "Wed", "Thu", "Fri"]
add_weekend(workdays)
print(workdays)
Output
['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']

Why Numbers and Text Never Surprise You

The same sharing happens with numbers and strings, but you can never see it, because those values cannot be changed in place. x = x + 1 does not alter the number; it makes a new number and moves the label. String methods, as Chapter 2 showed, return new strings. Only values that can change in place, such as lists and dictionaries, can show you that two names were sharing them.

Sharing one list versus owning a copy
Two names, one list
workdays and week both point at the same list, so an append through either shows in both.
A copy
Two lists, each with its own name, changed independently.
Common Confusions
  • "b = a copies the list." It gives the same list a second name. Use a.copy() or list(a) for a real copy.
  • "I should copy every list, just in case." Copy when you need two independent lists. Sharing is often exactly what you want.
  • "The same thing happens with numbers." It does, invisibly: numbers and strings cannot be changed in place, so there is nothing to see.
Why It Matters
  • This explains a whole class of bugs that look impossible, the "I never touched that list" kind.
  • The label picture is the key to reading any Python code that passes lists and dictionaries around, which is nearly all of it.

Knowledge Check

After b = a and b.append(4), where a was [1, 2, 3], what is a?

  • [1, 2, 3], because only b was changed
  • [1, 2, 3, 4], because a and b are the same list
  • An error, because the list now has two names
  • [4], because b replaced the list's contents

How do you make a list that can change without affecting the original?

  • b = a, which gives b its own list
  • b == a, which compares and then copies
  • b = a.append(), which returns a fresh list
  • b = a.copy(), which builds a new list

A function appends to a list it was given. What happens to the caller's list?

  • It changes too, because the function received the same list
  • Nothing, because functions always work on a copy
  • It is deleted when the function returns to the caller
  • It raises an error, because functions are not allowed to change lists

Why does sharing never cause surprises with numbers?

  • Python always copies numbers when assigning them
  • A number cannot change in place, so nobody can alter it
  • Numbers are kept in a different part of memory
  • Only one name at a time can point at a number

You got correct