Values, types and names · about 40 min

Names bind to objects

Why this matters

In many languages a variable is a box that holds a value, and b = a copies the value into another box. Python does not work that way, and programmers who carry the box picture into Python write bugs: a list "mysteriously" changes because some other part of the program modified it. The correct model is simple and covers every case: names are labels attached to objects. Learn it now and lists, function arguments, default values and copying (all coming later) will behave exactly as you predict.

Assignment is binding

x = 42

This statement does two things, in this order:

  1. Evaluate the right-hand side. Here that produces (or reuses) an int object with value 42.
  2. Bind the name x to that object: record in the current namespace that x refers to it.
  names                objects
  -----                -------
   x  ---------------> [ int | 42 ]

A namespace is the table mapping names to objects. At the top level of a script it is the module's global namespace (it really is a dictionary; globals() shows it). The name itself holds no data; it is just an entry pointing at an object. That pointer is called a reference.

Now a second assignment:

y = x

Evaluate the right-hand side: the name x evaluates to the object it refers to. Bind y to that same object. No copy is made.

   x  ---------\
                >----> [ int | 42 ]
   y  ---------/

Then rebinding:

x = x + 1

Evaluate x + 1: take the object x refers to (42), add 1, producing a new object 43. Bind x to it:

   x  -------------> [ int | 43 ]
   y  -------------> [ int | 42 ]

y is unaffected. Assignment never changes an object; it changes what a name refers to.

Identity: id() and is

Each object has an identity, available as id(obj). Two names refer to the same object exactly when their ids are equal. The operator is tests this directly:

a = [1, 2, 3]
b = a
c = [1, 2, 3]

print(a == c)   # same value?
print(a is c)   # same object?
print(a is b)
True
False
True

c = [1, 2, 3] built a second list that happens to have the same contents. Two objects, equal values.

   a ----\
          >----> [ list | 1, 2, 3 ]     id: 1001 (say)
   b ----/
   c ----------> [ list | 1, 2, 3 ]     id: 2002

Use == to compare values. Use is almost only for comparing with None (if x is None, next lessons), where there is exactly one such object.

Mutable and immutable objects

Some types allow an object's value to change in place; others never do.

With immutable objects, aliasing is invisible: nobody can change the shared 42, so it does not matter how many names refer to it. With mutable objects, aliasing is visible:

a = [1, 2]
b = a
b.append(3)
print(a)
print(b)
[1, 2, 3]
[1, 2, 3]

b.append(3) did not touch the name a or the name b. It changed the one list object they both refer to.

Compare with strings, which are immutable:

s = "abc"
t = s
t = t + "d"
print(s)
print(t)
abc
abcd

t + "d" cannot change the string "abc"; it creates a new string "abcd" and t is rebound to it.

Note: "Does this line rebind a name, or mutate an object?" is the question to ask whenever two names might share an object. name = ... rebinds. name.method(...) and name[i] = ... (later) may mutate.

Names have no type

Since a name is just a reference, it can refer to an object of any type, and switch:

thing = 42
print(type(thing))
thing = "forty-two"
print(type(thing))
<class 'int'>
<class 'str'>

The objects have types; the name thing merely pointed at two different objects in turn. That is what dynamic typing means. (Type hints, covered much later, let you document what a name is meant to refer to, but they do not change this behavior.)

del removes a name, not an object

a = [1, 2, 3]
b = a
del a
print(b)
print(a)
[1, 2, 3]
Traceback (most recent call last):
  File "main.py", line 5, in <module>
    print(a)
          ^
NameError: name 'a' is not defined

del a removes the binding for a from the namespace. The list survives because b still refers to it. Objects are freed automatically when nothing refers to them any more. In CPython each object keeps a reference count; when it drops to zero, the memory is reclaimed immediately. You never free memory by hand in Python.

A note on small ints and strings

You may notice this in the REPL:

>>> a = 256
>>> b = 256
>>> a is b
True
>>> a = 1000
>>> b = 1000
>>> a is b
False

CPython pre-creates the ints from -5 to 256 and reuses them, and it reuses some strings, purely as a memory optimization. Whether two equal immutable values are the same object is an implementation detail that changes with context and version. This is exactly why you must not use is to compare numbers or strings: it can appear to work and then fail. Python 3.8+ even warns: SyntaxWarning: "is" with 'int' literal. Did you mean "=="?

Common mistakes

Recap

Going further: paste the examples into pythontutor.com's visualizer (or draw them by hand) to see names and arrows. Ned Batchelder's talk "Facts and Myths about Python Names and Values" is the classic explanation of this model.

Exercises

Exercise 1 · quiz

Rebinding does not affect other names

What does this print?

x = 5
y = x
x = x + 1
print(y)
Answer
  • 5

y = x binds y to the same object x refers to, the int 5. x = x + 1 computes a new object, 6, and rebinds x to it. y was never touched and still refers to 5. Assignment changes which object a name refers to; it never changes the object.

Exercise 2 · quiz

Two names, one list

What does this print?

a = [1, 2]
b = a
b.append(3)
print(a)
Answer
  • [1, 2, 3]

b = a does not copy the list. It binds a second name to the same list object. b.append(3) mutates that one object, so looking at it through either name shows [1, 2, 3]. This is aliasing. With immutable objects like ints and strings, aliasing is harmless because nobody can change the shared object; with mutable objects like lists it matters a lot.

Exercise 3 · write code

Equal or identical?

Read two whole numbers from two lines. Build two lists and an alias like this:

a = [x, y]
b = [x, y]
c = a

Then print the results of a == b, a is b and a is c, each on its own line, labeled exactly as below. Finally append 0 to c with c.append(0) and print a with the label shown. For inputs 1 and 2:

a == b: True
a is b: False
a is c: True
a after c.append(0): [1, 2, 0]

File: main.py

Starting point

x = int(input())
y = int(input())
a = [x, y]

Example

input

1
2

expected output

a == b: True
a is b: False
a is c: True
a after c.append(0): [1, 2, 0]

Example

input

7
-3

expected output

a == b: True
a is b: False
a is c: True
a after c.append(0): [7, -3, 0]
Hint 1

print("a == b:", a == b) prints the label, a space and then True or False.

Hint 2

Each [...] expression creates a brand-new list object, even with the same contents.

Hint 3

c = a creates no object; it just binds a second name.

Reference solution
x = int(input())
y = int(input())
a = [x, y]
b = [x, y]
c = a
print("a == b:", a == b)
print("a is b:", a is b)
print("a is c:", a is c)
c.append(0)
print("a after c.append(0):", a)

Exercise 4 · quiz

What del deletes

After running

a = [1, 2, 3]
b = a
del a

which statement is true?

Answer
  • The name a is removed; the list still exists because b refers to it

del a unbinds the name a. The list object is only freed when no references to it remain, and b still refers to it, so print(b) shows [1, 2, 3] while print(a) raises NameError: name 'a' is not defined. Python has no statement that destroys an object directly.

Exercise 5 · quiz

How many objects?

How many distinct list objects exist after this code runs? Answer with a number.

p = [0]
q = p
r = [0]
s = r
t = q
Answer

Only the two list displays [0] create lists. q, t are names for the first list (the one p refers to), and s is a name for the second. Five names, two objects. Drawing names as labels with arrows to objects makes questions like this easy.