The snapshot that moved
Suppose a list holds share quantities for fictional assets A and B, always in that order. A report tries to save the list under another name:
positions = [2, 3] # shares of fictional assets A and B
cached_positions = positions
print(cached_positions is positions)
# True
cached_positions[0] = 5
print(positions, cached_positions)
# [5, 3] [5, 3]
positions = [8, 3]
print(positions, cached_positions)
# [8, 3] [5, 3]
print(cached_positions is positions)
# Falseis test checks object identity. When the cached name changes A's quantity from 2 to 5 shares, the live name sees that change too. The cache did not preserve the earlier positions.positions = [8, 3] behaves differently: it binds positions to a new list. The cached name still refers to the previous list, whose quantities are now [5, 3]. Python's assignment rules distinguish assigning to a name from assigning to an item's slot.Copy at the boundary you need to preserve
For this flat list of integers, a shallow copy is enough to keep an earlier report's quantities:
positions = [2, 3]
snapshot = positions.copy()
positions[0] = 5
print(positions, snapshot)
# [5, 3] [2, 3]
print(snapshot is positions)
# FalseThe lists are distinct objects. Replacing a slot in one list does not replace the corresponding slot in the other. The integer objects themselves are immutable; no recursive copying is needed for this example.
A shallow copy does have a boundary. Copying a dictionary creates a new outer dictionary while preserving references to its values. If a value is a list, that list can still be shared:
book = {"positions": [2, 3]}
snapshot = book.copy()
book["positions"][0] = 5
print(snapshot["positions"])
# [5, 3]
print(snapshot is book, snapshot["positions"] is book["positions"])
# False TrueMake the update contract visible
A function parameter can refer to the caller's list. Mutating that list can affect the caller even when the function returns nothing; merely rebinding the local parameter does not replace the caller's binding. Passing an input explicitly therefore makes the dependency visible, but does not by itself protect it from mutation.
Sources
Examples checked with Python 3.14.4. All position quantities are fictional.