Home > Mobile >  Python: Where are unassigned values stored?
Python: Where are unassigned values stored?

Time:08-25

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.

CodePudding user response:

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 location - when garbage collection time comes - any unlinked references are deleted, thus freeing the memory for later use.

Interactive interpreters:

A special thanks to @acrobat for pointing out the use of the special underscore variable.

Note: This section only applies to interactive Python interpreters. If this logic is used in a script or program, a NameError: name '_' is not defined error will be thrown.

In regard to interactive interpreters, the returned value from the latest execution is stored into the 'special' underscore variable: _. Therefore, the return value of s1.replace('R', 'B') is stored to _, as this was the last returned (unassigned) value. However, the next call which returns a value overwrites the underscore.

For example:

# Assignment example.
a = 'Robert'
a.replace('R', 'B')

# What does _ contain?
print(_)  # >>> 'Bobert'

# Get the object type; returns 'str'
type(_)  # >>> str

# 'str' is now stored, as this was the last returned value.
print(_)  # >>> str

Worked example:

Note: The underscore logic is not considered in these examples, due to the transient nature of the special variable.

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

CodePudding user response:

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
  • Related