Mutability, Copying, and the Object Model
Names, objects, identity, and why copying is never as simple as it looks
is-vs-== distinction, the mutable-default-argument trap, and the difference between shallow and deep copies all stop being surprises and become predictable consequences of one model.
- explain the difference between a name, an object, and a value;
- reason precisely about aliasing and when two names share one object;
- choose correctly between
is(identity) and==(equality); - recognise and fix the mutable default argument trap;
- pick the right tool among assignment,
copy.copy, andcopy.deepcopy.
1. Names bind to objects, they do not hold them
Every value in Python is an object with three attributes: an identity (its address, revealed by id()), a type, and the data it carries. A variable is simply a name in some namespace that refers to one of those objects. When you write b = a, Python does not duplicate the object a points at; it makes b refer to the very same object. Both names now sit on top of one object.
a = [1, 2, 3]
b = a # b refers to the SAME list object, not a copy
b.append(4)
print(a) # [1, 2, 3, 4] -- a "changed" because there is one list
print(id(a) == id(b)) # True -- same identity
This is not a special case for lists. It is how every assignment works, for integers, strings, functions and instances alike. The difference you feel between "value types" and "reference types" in other languages does not exist here: everything is a reference to an object. What differs is whether the object can be changed after creation.