Variables and Objects

It's pointers all the way down

Python Morsels
Truthful Technology

trey @ Python Morsels .com

© 2024 Akiyoshi Kitaoka, used with permission

https://www.psy.ritsumei.ac.jp/akitaoka/saishin72e.html

#7c7c7c

https://www.psy.ritsumei.ac.jp/akitaoka/saishin72e.html

Mental Models

β€œAll models are wrong,
but some models are useful.” — George E. P. Box

>>> first = []
>>> second = first
>>> second.append(9)
>>> second
[9]
>>> first
          

Buckets


>>> first = []
>>> second = first
>>> second.append(9)
>>> second
[9]
>>> first
[9]
first [9] second [9]

This mental model breaks down

Sticky Notes


>>> first = []
>>> second = first
>>> second.append(9)
>>> second
[9]
>>> first
[9]
>>> 
            
[9] first second

Pointers  πŸ€”


>>> first = []
>>> second = first
>>> second.append(9)
>>> second
[9]
>>> first
[9]
>>> rows = [second]
>>> 
            
Object Land Variable Land list first second [] [9]

well actually

“... but they're not pointers”

“They're object references”

“They're name bindings”

“But they're not like pointers in C”

binding πŸͺ’
reference πŸ“–
pointer πŸ‘‰

Ch-ch-ch-changes

“when I change first, it doesn't always change second


>>> first = [2, 1, 3, 4]
>>> second = first
>>> first.append(7)
>>> second
[2, 1, 3, 4, 7]
>>> first = [100, 200, 300]
>>> second
[2, 1, 3, 4, 7]
>>> 
          

>>> first = [2, 1, 3, 4]
>>> second = first
>>> first.append(7)
>>> second
[2, 1, 3, 4, 7]
>>> first = [100, 200, 300]
>>> second
[2, 1, 3, 4, 7]
>>>
            
Object Land Variable Land list second first [2, 1, 3, 4] [2, 1, 3, 4, 7] list [100, 200, 300]

Remember: variables point to objects

The 2 Types of Change

  • Mutations change an object
  • Assignments change a variable

2 types of

“are they the same?”


>>> first == second  # Equality
True
>>> first is second  # Identity
True

Identity: the exact same object

Equality: an equivalent object

Equality is about objects

Identity is about pointers


>>> first == second  # Are these objects equal to the other?
True
>>> first is second  # Do these variables point to the same object?
True

Equality:
the usual way to ask
“are they the same?”


value is None
          

value == another_value
          

Objects

Variables

“Change”

“Contains”


>>> row = [0, 0, 0]
>>> boat = [row, row, row]
>>> boat
[[0, 0, 0], [0, 0, 0], [0, 0, 0]]
>>> boat[1][1] = 1
>>> boat
[[0, 1, 0], [0, 1, 0], [0, 1, 0]]
>>> row
[0, 1, 0]

Sticky Notes


>>> first = []
>>> second = first
>>> second.append(9)
>>> second
[9]
>>> first
[9]
>>> rows = [second]
>>> lists = rows
[9] first second rows[0] lists[0]

Pointers


>>> first = []
>>> second = first
>>> second.append(9)
>>> second
[9]
>>> first
[9]
>>> rows = [second]
>>> 
            
Object Land Variable Land list first second int 9 0 rows list 0

Pointers


>>> first = []
>>> second = first
>>> second.append(9)
>>> second
[9]
>>> first
[9]
>>> rows = [second]
>>> 
            
Object Land Variable Land list first second int 9 0 rows list 0

Containment

... is a lie


>>> class TodoList:
...     def __init__(self, tasks):
...         self.tasks = tasks
... 
...     def add_task(self, task):
...         self.tasks.append(task)
...
>>> default_todos = ["Reflect on last week"]
>>> mon = TodoList(default_todos)
>>> tue = TodoList(default_todos)
>>> mon.add_task("Work on talk")
>>> mon.tasks
['Reflect on last week', 'Work on talk']
>>> tue.tasks
['Reflect on last week', 'Work on talk']


>>> class TodoList:
...     def __init__(self, tasks):
...        #self.tasks = tasks
...         self.tasks = list(tasks)  # shallowly copy the list
...     def add_task(self, task):
...         self.tasks.append(task)
...
>>> default_todos = ["Reflect on last week"]
>>> mon = TodoList(default_todos)
>>> tue = TodoList(default_todos)
>>> mon.add_task("Work on talk")
>>> mon.tasks
['Reflect on last week', 'Work on talk']
>>> tue.tasks
['Reflect on last week']
>>> wed = TodoList({"Here is", "a set", "of tasks"})
          

Objects don't contain objects

  • Variables point to objects
  • Objects can contain pointers to objects
  • A list-of-lists is a list of pointers to lists
  • x in y loops over y checking equality with x

Assignments happen


>>> n = 3
>>> from math import pi
>>> for n in range(5):
...     ...
...
>>> n
4
>>> def pi(): return 3.14159
...
>>> class pi: pass
...
          

Augmented Assignments

+=, -=, *=, etc.

Are these assignments?

Are these mutations?

YES

In-place addition


>>> a = [2, 1, 3]
>>> b = a
>>> b += [4, 7, 11]
>>> b
[2, 1, 3, 4, 7, 11]
>>> a
[2, 1, 3, 4, 7, 11]

>>> name = "North Bay Python"
>>> name += " 2025"  # name = name + "2025"
>>> name
'North Bay Python 2025'

Assignments that mutate


>>> some_object.attribute = 4
>>> numbers[0] = 8
            

Assignments can mutate

  • Assignments change the object a reference refers to
  • Objects can contain references to other object
  • Assigning to an object attribute/index mutates the object

Changing tuples

Tuples cannot be mutated*

* For some definitions of "mutate"

Tuples can contain lists


>>> result = (True, [2, 1, 3])
>>> result[1].append(4)
>>> result
(True, [2, 1, 3, 4])

Immutability is shallow


>>> result = (True, [2, 1, 3])
>>> result2 = (True, [2, 1, 3])
>>> result == result2
True
>>> result[1].append(4)
>>> result
(True, [2, 1, 3, 4])
>>> result == result2
False

It's pointers all the way down 🐒

  • Variables store object references
  • Objects store object references
  • Immutable containers can change

>>> x = []
>>> x.append(x)
>>> x
[[...]]
>>> x in x
True
>>> x[0] is x
True
          

>>> x = []
>>> x.append(x)
>>> x
[[...]]
>>> x[0] is x
True
>>> x in x
True
          

We didn't discuss

  • Variable scope in Python
  • Hashability and its relation to immutability
  • CPython optimizations that affect identity
  • Direct value storage of array, data frames, etc.
  • Mutable default argument gotchas
  • Python oddities related to variables and objects

https://trey.io/nbpy2025

Names, Objects, and Plummeting From The Cliff - Brandon Rhodes

Facts and Myths about Names and Values - Ned Batchelder

But... why?

Imagine an alternative...

Would every assignment statement make a copy?

Would storing a reference to an object ever be possible?

"reference assignments" and "copying assignments" 😬

This thing is bad

Compared to what alternative?

Everything is a trade-off

Resources   https://trey.io/nbpy2025

Thank you

Trey Hunner
Python Team Trainer
[email protected]

Gotchas with Variables and Values

Self-concatenation


>>> rows = [[0] * 3] * 3
>>> rows
[[0, 0, 0], [0, 0, 0], [0, 0, 0]]
>>> rows[1][1] = 1
>>> rows
[[0, 1, 0], [0, 1, 0], [0, 1, 0]]
          

Augmented assignments in tuples


>>> result = (True, [2, 1, 3, 4])
>>> result[1] += [7, 11, 18]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
    result[1] += [7, 11, 18]
    ~~~~~~^^^
TypeError: 'tuple' object does not support item assignment
>>> result
(True, [2, 1, 3, 4, 7, 11, 18])