Сообщить об ошибке.

Сюрпризы модуля ctypes в Python

В модуле ctypes есть "красные линии", где можно ожидать все что угодно, только не то, что должно происходить на самом деле.

Рассмотрим следующий пример:

>>> from ctypes import *
>>> class POINT(Structure):
...     _fields_ = ("x", c_int), ("y", c_int)
...
>>> class RECT(Structure):
...     _fields_ = ("a", POINT), ("b", POINT)
...
>>> p1 = POINT(1, 2)
>>> p2 = POINT(3, 4)
>>> rc = RECT(p1, p2)
>>> print(rc.a.x, rc.a.y, rc.b.x, rc.b.y)
# 1 2 3 4
>>> # поменяем точки местами
>>> rc.a, rc.b = rc.b, rc.a
>>> print(rc.a.x, rc.a.y, rc.b.x, rc.b.y)
# 3 4 3 4

Упс... Ожидаемое поведение - это вывод на печать 3 4 1 2. Что произошло? Вот строка rc.a, rc.b = rc.b, rc.a, где произошла какая-то мистика:

>>> temp0, temp1 = rc.b, rc.a
>>> rc.a = temp0
>>> rc.b = temp1

Обратите внимание, что temp0 и temp1 - это объекты, все еще использующие внутренний буфер объекта rc. Таким образом, выполнение rc.a = temp0 копирует содержимое буфера temp0 в буфер rc. Это, в свою очередь, изменяет содержимое temp1. Итак, последнее присвоение rc.b = temp1 не дает ожидаемого эффекта.

Имейте в виду, что при получении подобъектов из структур, объединений и массивов, подобъект не копируется, а извлекается объект-оболочка, которая обращается к базовому буферу корневого объекта.

Смотрим другой пример, который может взбесить не на шутку:

>>> from ctypes import *
>>> s = c_char_p()
>>> s.value = b"abc def ghi"
>>> s.value
# b'abc def ghi'
>>> s.value is s.value
# False

Обратите внимание, что объекты, созданные из ctypes.c_char_p, могут иметь значение только в байтах или целых числах.

Почему False? Экземпляры ctypes - это объекты, содержащие блок памяти плюс некоторые дескрипторы, обращающиеся к содержимому памяти. При хранении объекта Python в блоке памяти не сохраняется сам объект, вместо этого сохраняется содержимое объекта. При повторном доступе к содержимому каждый раз создается новый объект Python!

Подробнее об этом читайте в материале "Основные типы данных модуля ctypes", а именно, в описании атрибута SimpleCData.value базового класса всех основных типов данных модуля ctypes.