osdir.com


[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Python-Dev] ctypes: is it intentional that id() is the only way to get the address of an object?


I feel like I should clarify - not everyone who posted got it wrong, and
I understand there's a side discussion among those who are also
interested/participants in
https://discuss.python.org/t/demoting-the-is-operator-to-avoid-an-identity-crisis/86/
- but there was no of acknowledgement of Eryk Sun's correct and useful
answer which I find very disappointing and a great way to discourage
contributions.

We can, and should, do better, at least by thanking the person for their
response before running down a barely related side track.

On 17Jan.2019 2209, Steve Dower wrote:
> For everyone who managed to reply *hours* after Eryk Sun posted the
> correct answer and still get it wrong, here it is again in full.
> 
> As a bonus, here's a link to the place where this answer appears in the
> documentation:
> https://docs.python.org/3/library/ctypes.html#ctypes.py_object
> 
> Cheers,
> Steve
> 
> On 17Jan.2019 0550, eryk sun wrote:
>> On 1/17/19, Steven D'Aprano <steve at pearwood.info> wrote:
>>>
>>> I understand that the only way to pass the address of an object to
>>> ctypes is to use that id. Is that intentional?
>>
>> It's kind of dangerous to pass an object to C without an increment of
>> its reference count. The proper way is to use a simple pointer of type
>> "O" (object), which is already created for you as the "py_object"
>> type.
>>
>>     >>> ctypes.py_object._type_
>>     'O'
>>     >>> ctypes.py_object.__bases__
>>     (<class '_ctypes._SimpleCData'>,)
>>
>> It keeps a reference in the readonly _objects attribute. For example:
>>
>>     >>> b = bytearray(b'spam')
>>     >>> sys.getrefcount(b)
>>     2
>>     >>> cb = ctypes.py_object(b)
>>     >>> sys.getrefcount(b)
>>     3
>>     >>> cb._objects
>>     bytearray(b'spam')
>>     >>> del cb
>>     >>> sys.getrefcount(b)
>>     2
>>
>> If you need the address without relying on id(), cast to a void pointer:
>>
>>     >>> ctypes.POINTER(ctypes.c_void_p)(cb)[0] == id(b)
>>     True
>>
>> Or instantiate a c_void_p from the py_object as a buffer:
>>
>>     >>> ctypes.c_void_p.from_buffer(cb).value == id(b)
>>     True
>>
>> Note that ctypes.cast() doesn't work in this case. It's implemented as
>> an FFI function that takes the object address as a void pointer. The
>> from_param method of c_void_p doesn't support py_object:
>>
>>     >>> ctypes.c_void_p.from_param(cb)
>>     Traceback (most recent call last):
>>       File "<stdin>", line 1, in <module>
>>     TypeError: wrong type