Python's Object Model in Depth: Why Two Lines That Look the Same Behave Differently
Two lines of code. Same variable. Same operator. Completely different behavior. a = [ 1 , 2 , 3 ] b = a b += [ 4 ] # line A print ( a ) # [1, 2, 3, 4] x = 1 y = x y += 1 # line B print ( x ) # 1 Line A changes a . Line B does not change x . The only difference is whether the variable holds a mutable or immutable object. To understand why this happens, you need to understand how Python actually represents variables internally. Variables Are Not Boxes The box metaphor is how most introductory programming courses explain variables: a variable is a box that holds a value. You put 5 into the box called x . Later you can replace it with 10. In Python this metaphor is accurate for immutable types and dangerously misleading for mutable types. The more accurate model: a Python variable is a name that is bound to an object. The object exists independently of the name. Multiple names can be bound to the same object. Binding a name to a new object does not affect the old object or other names that reference it. You can inspect this directly: a = [ 1 , 2 , 3 ] b = a print ( id ( a ) == id ( b )) # True -- same object, two names x = 5 y = x print ( id ( x ) == id ( y )) # True -- both point to integer object 5 Both cases start the same: two names pointing to the same object. What happens next depends on whether you mutate the object or rebind the name. Two Fundamental Operations Every operation on a Python object falls into one of two categories. Mutation : the object at a given memory address is modified. All names pointing to that address see the change. Rebinding : a name is pointed at a different memory address. Other names pointing to the original address are unaffected. List methods like .append() , .extend() , .sort() , and item assignment lst[i] = x are mutations. Assignment with = is rebinding. The += operator is either mutation or rebinding depending on whether __iadd__ is implemented and the object is mutable. Tracing Through the Object Graph a = [[ 1 , 2 ], [ 3 , 4 ]]