Mutability and copying
Trace shared references, distinguish rebinding from mutation, and copy nested data intentionally.
By the end, you’ll be able to…
- Predict when two names observe the same list mutation.
- Distinguish identity from equal contents.
- Choose shallow or deep copying based on the structure you need to isolate.
01Two names can refer to one object
Assignment does not automatically duplicate data. When backup = tasks runs, both names refer to the same list. Appending through either name changes that one shared object, so both names reveal the change. Imagine two labels attached to the same basket rather than two baskets containing matching items. This model explains why a supposed backup can change unexpectedly.
Rebinding a name is different from mutating the object it refers to. After backup = ['Rest'], backup refers to a newly created list, while tasks still refers to the original. The equals sign binds a name; an operation such as append changes a list. Trace the objects and arrows separately if the values are difficult to follow. The distinction also applies when collections are passed into functions later.
02Equality and identity answer different questions
The == operator asks whether values compare equal. The is operator asks whether two references identify the very same object. Two separately constructed lists can have equal contents without being one list. Sharing is therefore not something you can infer merely from how print displays an object. Use identity checks to investigate aliases when learning, not as a substitute for value comparison.
For ordinary numbers and strings, compare values with ==. Python may reuse some immutable objects internally, and programs should not depend on those implementation choices. A common appropriate identity check is value is None, because None is a unique singleton used to represent absence. This lesson uses lists so the relationship between independent construction and shared identity is easy to see.
03A shallow copy duplicates the outer container
list.copy(), a full list slice, and list(existing_list) each create a new outer list containing references to the existing items. This is called a shallow copy. With a flat list of immutable values, it often provides exactly the independence you need: replacing or appending an outer item does not change the original list's arrangement.
Nested mutable items require more thought. Copying a list of rows gives you a new outer list, but each row is still shared. Replacing copy[0] changes one outer slot; changing copy[0][0] mutates a shared row. Ask which level of the structure your next operation will modify. A shallow copy is neither a broken copy nor a promise of complete independence; it has a specific boundary.
04Copy the level your task needs
The standard library's copy module provides deepcopy for recursively copying supported nested structures. The import line below makes that tool available; the modules lesson will explain imports in more depth. For these simple nested lists, deepcopy creates independent inner lists, so editing a copied row leaves the original data intact.
Do not reach for deep copying every time two names might share something. Sometimes shared state is intentional, and sometimes constructing a small new result is clearer than duplicating a large object graph. Deep copying can also copy more data than necessary and has special rules for some object types. State the requirement first: 'editing these copied rows must not edit the source rows.' Then choose an operation that fulfills that requirement and verify it with one small mutation.
The ideas worth keeping.
- Assignment can create another name for the same object.
- Mutation changes an object; rebinding changes which object a name refers to.
- Shallow copies isolate the outer container; nested mutable data may require a deeper copy.
Make it yours.
Type and test your code right here in the interactive workspace. Run your solution, check the output, and compare with the worked guide when ready.
Make a flat draft independent
Start with topics = ['strings', 'lists']. Create a shallow copy called draft, append functions to draft, and print both lists. topics must retain only its original two items.
Explore a worked solution
topics = ["strings", "lists"]draft = topics.copy()draft.append("functions")print(topics)print(draft)The two lists have independent outer structures. Sharing the immutable string items is harmless because appending changes only the draft's outer list.
Protect a nested grade table
Given grades = [['Ada', 7], ['Mira', 8]], make an independent copy named revised. Change Ada's score to 9 in revised and print both structures. The original score must remain 7.
Explore a worked solution
from copy import deepcopygrades = [["Ada", 7], ["Mira", 8]]revised = deepcopy(grades)revised[0][1] = 9print(grades)print(revised)A shallow outer copy would still share Ada's mutable row. deepcopy duplicates the nested list structure, which makes this particular inner mutation independent.
What stayed with you?
Check the ideas while they’re fresh. Your answers stay in this page and feedback appears as you go.
Make a flat draft independent
Start with topics = ['strings', 'lists']. Create a shallow copy called draft, append functions to draft, and print both lists. topics must retain only its original two items.
strings,lists topics.copy()
Keep exploring.
The official references behind this chapter, when you want a second angle or the full details.
Pause. Put the pieces together.
You’ve reached the end of Text and collections. Before moving on, use this task to check what you can do independently.
Clean a label, organize records, and demonstrate when a copy protects the original collection.
Try it in your own environment and write down the result. This is a self-check; reading completion does not evaluate your code.
Review this moduleOne chapter closer.
Reading is a start. Trying it yourself makes the connection.