A Python name refers to an object. b = a creates another binding to the same
object; it does not copy a list. Assignment can rebind a name, while a method
such as append changes the existing object. Indentation groups statements;
# starts a comment and print displays a value.
Follow the arrows: assignment shares an object; rebinding changes one arrow.
scores = [1, 2]
alias = scores
alias.append(3)
print(scores) # [1, 2, 3]
alias = [9]
print(scores) # still [1, 2, 3]Use == for values. is asks whether two references name the same object;
reserve is None for the absence sentinel. Integers, floats, strings and
tuples are immutable, but a tuple can still contain a mutable list.
A list slice copies the outer list. For a matrix represented by lists of lists, copying just the outer list still shares the rows:
matrix = [[1, 2], [3, 4]]
shallow = matrix[:]
shallow[0][0] = 7
independent = [row[:] for row in matrix]
independent[0][0] = 8
print(matrix) # [[7, 2], [3, 4]]Function defaults are evaluated when the function is defined. A default []
can therefore collect data across calls. Use None, then construct a fresh
container in the call. A function starts with def name(arguments): and
returns a result with return. The build's signatures are supplied; focus
on how many independently mutable containers each result needs.
In an experiment, aliasing a baseline's data into an intervention can change both conditions. The outputs can look plausible while the comparison loses its meaning. Test independence by mutating one output and inspecting its sibling and input, rather than checking only initial equality.
For nested data, copying the outer container still leaves the original rows shared.
Before the fill: draw the outer list and each row as separate boxes. For
[[0]] * 3, how many inner lists exist? Then implement a row copy and a
configuration update without changing the caller's values. These tasks use
numeric rows and flat dictionaries; recursively copying arbitrary objects
is outside their contract.