Programming · free software · the open webSubscribe via RSS

A Python name is a binding, not a box containing a copy

Python assignment binds names to objects, so mutating a shared list is visible through every alias while rebinding one name changes only that binding.

Assignment connects a name to an object

It is tempting to picture a variable as a box that owns its value. A more useful model is that names are bindings to objects. Python represents data as objects, each with identity, type and value; lists are mutable objects whose contents can change. [1]

When b = a runs, Python does not copy the list. It makes b refer to the same list object as a. At this point, using either name to mutate that list affects the object both names reach. The binding is shared; the list is not duplicated.

Mutation follows the shared reference

In the example, a begins as a list and b is bound to that same object. The call to a.copy() creates a new outer list for c. When b.append(3) mutates the shared list, printing a produces [1, 2, 3], while printing c produces [1, 2]. The copy captured the original sequence of elements before the append. Lists are mutable, and a shallow copy creates a separate outer container. [1]

The word “shallow” matters if elements are themselves mutable. For example, copying a list of dictionaries creates a new list but retains references to the same dictionaries. Replacing an element in one outer list does not replace it in the other, but mutating a shared dictionary is visible through both. Copy depth describes which reference edges are duplicated, not whether every object in a nested structure is independent.

a = [1, 2]
b = a
c = a.copy()
b.append(3)
print(a)       # [1, 2, 3]
print(c)       # [1, 2]
b = [9]
print(a is b)  # False
Locally checked Python example · Download example

Rebinding changes one name’s target

Later, b = [9] binds b to a newly created list. It does not reach backward and alter the original list or change what a refers to. The identity test a is b therefore prints False: these names now refer to distinct list objects. Python defines is as an identity comparison, while value equality asks whether objects compare equal. [1]

That distinction is practical when debugging state. If two lists have equal contents, a == b can be true even when a is b is false. Conversely, aliases can satisfy both identity and equality before either list changes. Use equality to ask whether values match; use identity when the question is whether two references denote the very same object.

Read the output as a reference trace

The example’s output is exactly [1, 2, 3], then [1, 2], then False. The first line follows mutation through an alias; the second follows the independent outer list made earlier; the last follows rebinding to a new list. Thinking in terms of object identity makes all three results predictable without imagining assignment as an implicit copy.

This model also clarifies APIs that accept mutable arguments. A function receiving a list can mutate the object visible to its caller, even if the local parameter has another name. If the function instead assigns a new list to its local parameter, that rebinding alone does not change the caller’s binding. The key question is always whether an operation changes an object or redirects a name.

References

  1. Python data model

Suggest a correction · All posts