1. Python Memory Model
Introduction
Data
Three components:
- id: unique identifier, often a memory address
print(id(3))
print(id("words"))
- type: determines what functions can operate on it
print(type(3))
print(type('words'))
- value: accessing the value is known as evaluating an object
Variable
Not an object so does not store data. Stores id that refers to an object that stores data. Variables do not have a type. Essentially means "go to this id".
Mutability and Aliasing
Strings are immutable, so will change the object it references
prof = 'Diane'
print(id(prof))
prof = prof + 'Horton'
print(id(prof))
For immutable objects we make sure to draw a double box
Mutable objects will keep the same ID.
x = [1, 2, 3]
print(id(x))
x[0] = 10000
print(id(x))
x.extend([4, 5, 6])
print(id(x))
Aliasing is when two variables point to the same object
x = [1, 2, 3]
y = [1, 2, 3]
z = x
print(id(x),id(y),id(z))
Modifying the mutable object referenced by z will modify x which is a side-effect of aliasing
Equality
Value equality == checks if the values stored in the objects are equal
Identity Equality is
checks if the ids of the objects are the same for the objects that they reference.
Functions and Parameters
# Example 1.
def mess_about(n: int, s: str) -> None:
message = s * n
print (message)
if __name__ == '__main__':
count = 13
word = 'nonsense'
mess_about (count, word)
Each variable in the parentheses is a parameter, and when we call the function, each expression in the parentheses is an argument.
Python Types: Mutable vs Immutable
Type | Mutable? | Examples | Notes |
---|---|---|---|
int |
No | 1 , 42 , -7 |
New object created on change |
float |
No | 3.14 , -2.0 |
Same behavior as int |
bool |
No | True , False |
Subclass of int |
str |
No | "hello" , 'world' |
Strings are immutable |
tuple |
No | (1, 2) , ("a", [1, 2]) |
May contain mutable elements |
frozenset |
No | frozenset([1, 2, 3]) |
Immutable version of set |
bytes |
No | b'abc' |
Immutable byte sequence |
list |
Yes | [1, 2, 3] , [] |
Can append, pop, modify elements |
dict |
Yes | {"a": 1} , {} |
Keys must be immutable |
set |
Yes | {1, 2, 3} , set() |
Unordered, no duplicates |
bytearray |
Yes | bytearray(b"abc") |
Mutable version of bytes |
defaultdict , OrderedDict |
Yes | From collections module |
Mutable dictionaries with extra functionality |
DataFrame , Series (Pandas) |
Yes | df['col'] = ... |
Changes propagate in place — use caution |
Tracking function calls
Stack Frame: stores information of the function that is currently running and any variables defined inside of it.
Every time we call a function, the following happens:
- A new frame is created and placed on top of any frames that may already exist. We call this pile of frames the call stack.
- Each parameter is defined inside that frame.
- The arguments in the function call are evaluated in order from left to right. Each is an expression, and evaluating it yields the id of an object. Each of these ids is assigned to the corresponding parameter.
Based on our previous example before printing message:

If the variable on the left-hand-side of the assignment doesn’t already exist in the top stack frame, Python will create it in that top stack frame. Here message
is a local variable.
When a function returns, the frame for that call and its variables are deleted.
Passing an argument creates an alias
If an argument is a variable, we assign the ID of the object it references to the function's parameter. If it's mutable, we have to worry about side effects.