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

Структуры Structure и Union модуля ctypes в Python

Структурированные типы данных модуля ctypes

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