Question presented to candidate:
"If I have a list of lists and I write b = a, b = a[:] or b = copy.deepcopy(a), what is different about b in each case? When does a shallow copy bite you, and why not just deep-copy everything?"
What a strong answer should cover:
- Assignment binds a second name to the same object; nothing is copied (
b is aisTrue) - A shallow copy (
copy.copy,list(x),x[:],x.copy(),dict(x),{**d}) builds a new outer container whose slots point at the same inner objects - A deep copy (
copy.deepcopy) recursively copies everything reachable, using a memo dict so shared references stay shared and cycles do not loop forever - Consequence: mutating a nested object leaks through a shallow copy, but rebinding a top-level slot does not
- Costs and limits: deep copy is slower and uses more memory; immutable objects (tuples of immutables, ints, strings) come back as the same object; functions and classes are not duplicated; modules, locks and sockets cannot be copied;
__copy__/__deepcopy__(memo)customise it - Bonus:
copy.replace()(Python 3.13) for a modified shallow copy, and the[[]] * 3trap
Clarifying questions expected:
- Is the structure nested, and does it contain mutable objects or resources (files, connections, locks)?
- Does the copy need to be independent, or is sharing the inner objects acceptable (or even desired)?
Code / implementation expected: Optional β a nested list showing a shallow copy leaking a nested mutation and a deep copy staying independent.