Python path · Objects and execution · Topic 2 of 12

Names, objects and references

Not started

In Python every value is an object living somewhere in memory, and a variable is just a name bound to one of those objects. Assigning, passing an argument or putting something in a list all copy a reference, never the object. Once you picture names as arrows, aliasing, mutability and the difference between is and == stop being surprising.

Why it matters: Most "Python changed my data behind my back" bugs come from two names pointing at one mutable object.

  1. 1Everything is an object

    Ints, functions, classes and modules all have an identity, a type and a value.

  2. 2Names point, they don't contain

    Assignment binds a name to an object. It never copies the object.

  3. 3Mutable or not

    Lists, dicts and sets can change in place; ints, strings and tuples cannot. Two names on one mutable object see each other's changes.

  4. 4is vs ==

    `is` asks whether two names point at the same object; `==` asks whether the values are equal.

1

How it works

The explanation, with examples you can run.

Everything is an object

Every object has three things: an identity (fixed for its lifetime; id() returns it, and in CPython it is the memory address), a type (which decides what operations it supports) and a value. That really does mean everything: type(len) is builtin_function_or_method, type("abc") is str, and type(str) is type. Functions, classes and modules are objects you can store in a dict or pass to another function, which is what makes decorators and plugin registries possible.

Names are arrows

a = [1, 2]b = a              # a second name for the same listb.append(3)print(a, a is b)   # [1, 2, 3] True c = list(a)        # a new list with the same itemsprint(c == a, c is a)   # True False

b = a did not copy the list; it made a second arrow to the same object, so a change through b is visible through a. list(a) builds a new object, so it is equal (==) but not identical (is). Function calls work the same way: the parameter is a new name for the caller's object. If the function mutates it, the caller sees the change; if the function rebinds the name, it doesn't.

Mutable and immutable

Lists, dicts, sets and most user-defined objects are mutable: they can change in place. Ints, floats, strings, tuples and frozensets are immutable: any "change" builds a new object. Immutability is shallow, though. A tuple can hold a list, and that list can still change. That leads to a famous oddity: t = (1, [2]); t[1] += [3] raises TypeError: 'tuple' object does not support item assignment, yet afterwards t is (1, [2, 3]). The list was extended in place first, and only the step that stores the result back into the tuple failed.

Augmented assignment is where the difference shows. For a list, x += [3] extends it in place (every alias sees it); for an int or a tuple there is no in-place version, so x += 1 builds a new object and rebinds only that name.

Default arguments are shared

def add_item(x, items=[]):    items.append(x)    return items print(add_item(1))   # [1]print(add_item(2))   # [1, 2]: the default list is shared between calls

The default list is created once, when def runs, and stored on the function. Every call that omits items mutates that same list. The fix is items=None and if items is None: items = [] inside the body.

is vs ==

== calls the type's __eq__ and compares values. is compares identities and can't be overridden. Use is only for singletons like None, True, False or a sentinel object you created. CPython happens to reuse some small ints and strings, so x is 5 may appear to work and then fail for larger numbers (1000 is int("1000") is False). Python 3.8 and later warn about is with a literal for exactly this reason.

2

Check yourself

3 questions. Get them all right to finish this topic.

  1. 1.What does this print?
    a = [1, 2]b = aa = a + [3]print(b)
  2. 2.Why is `def f(items=[])` a classic bug?
  3. 3.Which comparison is the right one for "is this value missing?"

0 of 3 right so far. Get all three to finish this topic.