A memory location is the address associated with an object while that object exists. Python’s built-in id() reports an object’s identity; in CPython, this is the object’s memory address at that moment. Calling id() on the same object returns the same value during its lifecycle, even when the object’s data changes. section 3.4.7 section 6.1.3
Use memory locations to investigate whether two names refer to the same object:
- Compare identities with is when the question is whether two references point to one object. section 10.3.2
- Compare id(a) and id(b) when inspecting object addresses; equal values indicate the same identity in these examples. section 6.1.3 section 10.3.2
- Do not confuse identity with equality: == compares values, while is checks object identity. section 10.3.2
Following one object through a method call
class Task: def __init__(self): print(f"Memory address (self): {id(self)}") task = Task() print(f"Memory address (task): {id(task)}") Both printed values match: self inside __init__ and task outside the method refer to the same instance. Python creates that instance through __new__ and passes it to __init__ as self. section 8.1.1
Listing 6.2 Using the same mutable object defined in the function
def append_task(task, tasks=[]):
tasks.append(task)
print(f"Tasks: {tasks}; id: {id(tasks)}") #1
append_task.__defaults__ #2
# output: ([],)
id(append_task.__defaults__[0])
# output: 4356663616
append_task("Homework")
# output: Tasks: ['Homework']; id: 4356663616
append_task("Laundry")
# output: Tasks: ['Homework', 'Laundry']; id: 4356663616
append_task.__defaults__
# output: (['Homework', 'Laundry'],)
Inspect a mutable default argument and watch the same list retain its memory address across calls, exposing unintended shared state. section 6.1.3


