Конкретные типы структур и объединений должны быть созданы путем создания подкласса одного из представленных ниже типов и по крайней мере определения переменной класса ._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_
, иначе она не будет иметь никакого эффекта.
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>