Updating a variable by its id within python
Asked Answered
E

3

8

I know how to get the value of a variable by its id in Python like:

a = "hello world!"
ctypes.cast(id(a), ctypes.py_object).value

I wonder if it possible to overwrite the variables value by id?

The simplest way, this one:

ctypes.cast(id(a), ctypes.py_object).value = "new value"

does not work.

Electrophilic answered 15/4, 2018 at 11:51 Comment(0)
D
10

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.

Delectate answered 15/4, 2018 at 18:38 Comment(9)
@user2357112 Which version of Python are you using, I cannot reproduce the error in your editFleeta
2.7.11 on Windows.Baez
I added the version since it seems to works for Python 3.6Fleeta
I get a crash on Ideone as well, Python 3.5, apparently some flavor of Linux. Maybe try it in a fresh interpreter. It could have been affected by other things you did, like mutate(1, 2).Baez
Yes, mutate(1, 2) is probably the cause since it makes Python exit with code -1073740940. My guess is that by mutating an integer, we may have mutated the exit codeFleeta
mutate(1, 2) doesn't seem to affect the crash on my setup; mutate([1], [1]) crashes with or without mutate(1, 2), and mutate(1, 2) doesn't (immediately) provoke a crash.Baez
Yes, I've been toying with it a bit now and there are just so many ways to make Python crash or throw senseless errors that I don't think it's worth digging deeper.Fleeta
This is awesome.Stephanstephana
Sorry to dig this up, I couldn't get it to run without error, apparently the for loop needs to start at 1 not 0 for this to work properly in python3.6Discontented
N
2

a is a string, and strings are immutable in Python.

Example from documentation:

>>> s = "Hello, World"
>>> c_s = c_wchar_p(s)
>>> print(c_s)
c_wchar_p(139966785747344)
>>> print(c_s.value)
Hello World
>>> c_s.value = "Hi, there"
>>> print(c_s)              # the memory location has changed
c_wchar_p(139966783348904)
>>> print(c_s.value)
Hi, there
>>> print(s)                # first object is unchanged
Hello, World
>>>
Natie answered 15/4, 2018 at 12:0 Comment(0)
E
0

use a list like this

>>> import ctypes
>>> a = ["hello world!"]
>>> ctypes.cast(id(a), ctypes.py_object).value[0] = "new value"
>>> print(a)
['new value']
Eohippus answered 5/9 at 13:6 Comment(1)
Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.Opinicus

© 2022 - 2024 — McMap. All rights reserved.