Python: Where are unassigned values stored?

Question:

This is the common example for string immutability

>>> s1='Robert'
...            
>>> id(s1)
...            
2039784292400
>>> s1.replace('R','B')
...            
'Bobert'
>>> id(s1)
...            
2039784292400
>>> s1
...            
'Robert'

Is there a way to find the id of the object holding the value ‘Bobert’?

Edit: I do know I can assign it to another variable. I was wondering if there is a default object where such values are stored and if it is possible to get the id of such objects.

Asked By: Monty Swanson

||

Answers:

Since you are not storing the replaced valued, it would be already in the garbage collection.

So, store it, and find its id.

a = 'Robert'

>>> id(a)
2554774561584

>>> b = a.replace('R', 'B')
>>> id(b)
2554774568432
Answered By: Nitish

The short answer:

There is not a ‘default object’ into which Python interpreter stores values (memory addresses) which have not been assigned.

But rather, the interpreter executes a statement (for example s1.replace('R', 'B')) and stores the result ('Bobert') into a memory location; which can be viewed as: id(s1.replace('R', 'B')). However, as there are no references pointing to that memory locationwhen garbage collection time comes – any unlinked references are deleted, thus freeing the memory for later use.

Worked example:

import ctypes

# Create the initial string object.
s1 = 'Robert'
s1_addr = id(s1)
print(f'{s1=} {s1_addr=}')

# Get the memory address to which the replacement points.
s2_addr = id(s1.replace('R', 'B'))
print(f'{s2_addr=}')

# Display (memory address) reference counts for each address.
s1_refs = ctypes.c_long.from_address(s1_addr).value
s2_refs = ctypes.c_long.from_address(s2_addr).value
print(f'{s1_refs=} {s2_refs=}')

Output:

s1='Robert' s1_addr=139940475739184
s2_addr=139940484802928
s1_refs=1 s2_refs=0

As you can see above, the s1 string is stored to a memory address. The replacement is also stored to a memory address. However, when it comes time to count the number of objects which point to each given address (as garbage collection will do), there are no references (objects) pointing to s2_addr, so the memory will be freed when gc is called.

Digging a little deeper:

If you refer to the bytecode for the following statements:

# Not using assignment.
s1 = "Robert"; s1.replace("R", "B")
# Using assignment.
s1 = "Robert"; s2 = s1.replace("R", "B")

You’ll note the following:

>>> dis('s1 = "Robert"; s1.replace("R", "B")'

  1           0 LOAD_CONST               0 ('Robert')
              2 STORE_NAME               0 (s1)
              4 LOAD_NAME                0 (s1)
              6 LOAD_METHOD              1 (replace)
              8 LOAD_CONST               1 ('R')
             10 LOAD_CONST               2 ('B')
             12 CALL_METHOD              2       # <-- str.replace called.
                                                 # <-- No reference created.
             14 POP_TOP
             16 LOAD_CONST               3 (None)
             18 RETURN_VALUE

You’ll note the str.replace method was called and thus acted upon the string, however as the STORE_NAME instruction was not called, an object reference is not created to its memory address.

In this example, note the call to STORE_NAME:

>>> dis('s1 = "Robert"; s2 = s1.replace("R", "B")')

  1           0 LOAD_CONST               0 ('Robert')
              2 STORE_NAME               0 (s1)
              4 LOAD_NAME                0 (s1)
              6 LOAD_METHOD              1 (replace)
              8 LOAD_CONST               1 ('R')
             10 LOAD_CONST               2 ('B')
             12 CALL_METHOD              2       # <-- str.replace called.
             14 STORE_NAME               2 (s2)  # <-- Object reference created here.
             16 LOAD_CONST               3 (None)
             18 RETURN_VALUE
Answered By: S3DEV
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.