Why it did not work
The object ctypes.cast(id(a), ctypes.py_object)
provides a view to an object in memory. A view is an object in its own rights. So when updating the value
attribute you do not update the object itself, all you do is create a new object and make the value
attribute of your view point to it.
import ctypes
a = "Hello World!"
py_obj = ctypes.cast(id(a), ctypes.py_object)
id(py_obj.value) # 1868526529136
py_obj.value = 'Bye Bye World!'
# Here we can see that `value` now points to a new object
id(py_obj.value) # 1868528280112
How to mutate any object
It is possible, with ctypes
, to update memory directly and thus to mutate any object. That is even true for strings which are said to be immutable.
The following is interesting as an exercice, but should never be used in other circumstances. Among other things it can corrupt object reference count, leading to memory management errors.
import ctypes
import sys
def mutate(obj, new_obj):
if sys.getsizeof(obj) != sys.getsizeof(new_obj):
raise ValueError('objects must have same size')
mem = (ctypes.c_byte * sys.getsizeof(obj)).from_address(id(obj))
new_mem = (ctypes.c_byte * sys.getsizeof(new_obj)).from_address(id(new_obj))
for i in range(len(mem)):
mem[i] = new_mem[i]
Here are examples. Among these you will find reasons why you must not use the above code unless you really know what you are doing or as an exercice.
s = 'Hello World!'
mutate(s, 'Bye World!!!')
print(s) # prints: 'Bye World!!!'
# The following happens because of Python interning
mutate('a', 'b')
print('a') # prints: 'b'
mutate(1, 2)
print(1) # prints: 2
In particular, the above example makes Python exit with an unknown error code or crash, depending on the version and environment.