В модуле 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
.