Конкретные типы структур и объединений должны быть созданы путем создания подкласса одного из представленных ниже типов и по крайней мере определения переменной класса ._fields_. Модуль ctypes создаст дескрипторы, которые позволят читать и записывать поля путем прямого доступа к атрибутам.
Конструкторы классов ctypes.Structure и ctypes.Union принимают как позиционные, так и ключевые аргументы.
._fields_.._fields_ с тем же именем или создают новые атрибуты для имен, отсутствующих в Structure._fields_.Выравнивание структуры/объединения и порядок байтов.
По умолчанию поля структуры и объединения выравниваются так же, как это делает компилятор C. Это поведение можно изменить, указав атрибут класса Structure._pack_ в определении подкласса. Значение должно быть положительное целое число и указывает максимальное выравнивание для полей. Это то, что #pragma pack(n) делает в MSVC.
Модуль ctypes использует собственный порядок байтов для классов ctypes.Structure и ctypes.Union. Для создания структур с неродным порядком байтов можно использовать один из базовых классов ctypes.BigEndianStructure, ctypes.LittleEndianStructure, ctypes.BigEndianUnion и ctypes.LittleEndianUnion. Эти классы не могут содержать поля указателей.
Предупреждение Модуль ctypes не поддерживает передачу объединений или структур с битовыми полями в функции по значению. Хотя это может работать на x86 32-bit, библиотека не гарантирует работу в общем случае. Объединения и структуры с битовыми полями всегда должны передаваться в функции по указателю.
ctypes.Union() объединения в собственном порядке байтов,ctypes.BigEndianStructure() структуры big с прямым порядком байтов,ctypes.LittleEndianStructure() структуры little с прямым порядком байтов,ctypes.Structure() структуры в собственном порядке байтов,ctypes.Union(*args, **kw):Класс ctypes.Union() представляет собой абстрактный базовый класс для объединений в собственном порядке байтов.
ctypes.BigEndianStructure(*args, **kw):Класс ctypes.BigEndianStructure() представляет собой абстрактный базовый класс для структур big с прямым порядком байтов.
ctypes.LittleEndianStructure(*args, **kw):Класс ctypes.LittleEndianStructure() представляет собой абстрактный базовый класс для структур little с прямым порядком байтов.
Структуры с неродным порядком байтов не могут содержать поля типа указатель или любые другие типы данных, в которых есть поля с указателями.
ctypes.Structure(*args, **kw):Класс ctypes.Structure() представляет собой абстрактный базовый класс для структур в собственном порядке байтов.
Подклассы структур наследуют поля базового класса. Если в определении подкласса есть отдельная переменная Structure._fields_, то указанные в ней поля добавляются к полям базового класса.
Structure_:Structure._fields_:Атрибут класса Structure._fields_ представляет собой последовательность, которая определяет поля структуры.
Элементы должны состоять из кортежей с двумя или тремя элементами. Первый элемент - это имя поля, второй элемент определяет тип поля. Это может быть любой тип данных модуля ctypes.
Для полей целочисленного типа, таких как ctypes.c_int, можно указать третий необязательный элемент. Это должно быть небольшое положительное целое число, определяющее разрядность поля.
Имена полей должны быть уникальными в пределах одной структуры или объединения. Только одно поле может быть доступно при повторении имен полей.
Можно определить переменную класса Structure._fields_ после оператора класса, который определяет подкласс структуры, это позволяет создавать типы данных, которые прямо или косвенно ссылаются на себя:
class List(Structure): pass List._fields_ = [("pnext", POINTER(List)), ... ]
Переменная класса Structure._fields_ должна быть определена до первого использования типа (создается экземпляр, для него вызывается sizeof() и т. д.). Последующее присвоение переменной класса Structure._fields_ вызовет ошибку AttributeError.
Можно определить подклассы типов структур, они будут наследовать поля базового класса плюс Structure._fields_, определенные в подклассе, если таковые имеются.
Structure._pack_:Атрибут класса Structure._pack_ это необязательное небольшое целое число, позволяющее переопределить выравнивание полей структуры в экземпляре.
Переменная Structure._pack_ должна быть уже определена при назначении Structure._fields_, иначе она не будет иметь никакого эффекта.
Особенности:
_fields_ (иначе не окажет эффекта).0 эквивалентно отсутствию указания _pack_.Устарело с Python 3.14, будет удалено в версии 3.19. По историческим причинам, если
_pack_не равен нулю, по умолчанию будет использоваться макет, совместимый с MSVC. На платформах, отличных от Windows, это значение по умолчанию устарело и в Python 3.19 будет считаться ошибкой. Если требуется MSVC-совместимость, то вместоStructure._pack_явно укажитеStructure._layout_ = "ms".
Structure._align_:Добавлено в Python 3.13.
Атрибут класса Structure._align_ представляет собой необязательное небольшое целое число для управления выравниванием структуры при чтении/записи в память.
Значение
0отключает эффект (аналогично отсутствиюStructure._align_).
Управление выравниванием
class AlignedStruct(Structure): _align_ = 16 # Выравнивание структуры по 16 байтам _fields_ = [ ("data", c_int32 * 4) ]
Structure._layout_:Добавлено в Python 3.14
Атрибут класса Structure._layout_ представляет собой необязательную строку, определяющую расположение (layout) структуры/объединения.
Допустимые значения:
"ms" - расположение, используемое компилятором Microsoft (MSVC).__attribute__((ms_struct))."gcc-sysv" - расположение GCC с моделью данных System V (используется в Linux/macOS).Structure._pack_ был 0 или не задан.Правила выбора по умолчанию:
"ms"."gcc-sysv" (если Structure._pack_ не задан или равен 0).Structure._pack_ ненулевой, используется "ms" (но это устаревшее поведение).Важно: Должен быть определен до присвоения
_fields_.
MSVC-совместимая структура (Windows)
from ctypes import Structure, c_int32 class MyStruct(Structure): _layout_ = "ms" # Явное указание MSVC-расположения _pack_ = 4 # Выравнивание в 4 байта (только для Windows!) _fields_ = [ ("field1", c_int32), ("field2", c_int32) ]
GCC-совместимая структура (Linux/macOS)
class MyStruct(Structure): _layout_ = "gcc-sysv" # System V (GCC) _fields_ = [ ("field1", c_int32), ("field2", c_int32) ]
Structure._anonymous_:Переменная класса Structure._anonymous_ это необязательная последовательность, в которой перечислены имена безымянных (анонимных) полей.
Переменная Structure._anonymous_ должна быть уже определена при назначении Structure._fields_, иначе она не будет иметь никакого эффекта.
Поля, перечисленные в этой переменной, должны быть полями типов структур или объединений. Модуль ctypes создаст дескрипторы в структуре, которые позволяют напрямую обращаться к вложенным полям без необходимости создания поля структуры или объединения.
Пример для Windows:
class _U(Union): _fields_ = [("lptdesc", POINTER(TYPEDESC)), ("lpadesc", POINTER(ARRAYDESC)), ("hreftype", HREFTYPE)] class TYPEDESC(Structure): _anonymous_ = ("u",) _fields_ = [("u", _U), ("vt", VARTYPE)]
Структура TYPEDESC описывает тип данных COM, поле vt указывает, какое из полей объединения является допустимым. Поскольку поле u определено как анонимное, то теперь можно получить доступ к членам непосредственно из экземпляра TYPEDESC. Вызовы td.lptdesc и td.u.lptdesc эквивалентны, но первый работает быстрее, так как ему не нужно создавать временный экземпляр объединения:
td = TYPEDESC() td.vt = VT_PTR td.lptdesc = POINTER(some_type) td.u.lptdesc = POINTER(some_type)
Структуры и объединения должны быть производными от базовых классов ctypes.Structure и ctypes.Union, которые определены в модуле ctypes. Каждый подкласс должен определять атрибут Structure._fields_. Переменная Structure._fields_ должна быть списком из парных кортежей, содержащим имя поля и тип поля.
Тип поля должен быть типом ctypes, например ctypes.c_int, или любым другим производным типом ctypes: структурой, объединением, массивом Array, указателем Pointer.
Вот простой пример структуры POINT, которая содержит два целых числа с именами x и y, а также показывает, как инициализировать структуру в конструкторе:
>>> from ctypes import * >>> class POINT(Structure): ... _fields_ = [("x", c_int), ... ("y", c_int)] ... >>> point = POINT(10, 20) >>> print(point.x, point.y) # 10 20 >>> point = POINT(y=5) >>> print(point.x, point.y) # 0 5 >>> POINT(1, 2, 3) # Traceback (most recent call last): # File "<stdin>", line 1, in <module> # TypeError: too many initializers
Можно строить гораздо более сложные конструкции. Структура может сама содержать другие структуры, используя структуру как тип поля.
Вот структура RECT, которая содержит две структуры POINT с именами upperleft и lowerright:
>>> class RECT(Structure): ... _fields_ = [("upperleft", POINT), ... ("lowerright", POINT)] ... >>> rc = RECT(point) >>> print(rc.upperleft.x, rc.upperleft.y) # 0 5 >>> print(rc.lowerright.x, rc.lowerright.y) # 0 0
Вложенные структуры также можно инициализировать в конструкторе несколькими способами:
>>> r = RECT(POINT(1, 2), POINT(3, 4)) >>> r = RECT((1, 2), (3, 4))
Дескрипторы полей могут быть получены из класса, они полезны для отладки, т. к. могут предоставить полезную информацию:
>>> print(POINT.x) # <Field type=c_long, ofs=0, size=4> >>> print(POINT.y) # <Field type=c_long, ofs=4, size=4>
Можно создавать структуры и объединения, содержащие битовые поля. Битовые поля возможны только для целочисленных полей, разрядность указывается в третьем элементе кортежей Structure._fields_:
>>> class Int(Structure): ... _fields_ = [("first_16", c_int, 16), ... ("second_16", c_int, 16)] ... >>> print(Int.first_16) # <Field type=c_long, ofs=0:0, bits=16> >>> print(Int.second_16) # <Field type=c_long, ofs=0:16, bits=16>