ctypes — Библиотека внешних функций для Python

Исходный код: Lib/ctypes


ctypes - это библиотека внешних функций для Python. Она предоставляет типы данных, совместимые с C, и позволяет вызывать функции из библиотек DLL или общих библиотек. Ее можно использовать для перевода этих библиотек в чистый Python.

учебное пособие по ctypes

Примечание: В примерах кода в этом руководстве используется doctest, чтобы убедиться, что они действительно работают. Поскольку некоторые примеры кода ведут себя по-разному в Linux, Windows или macOS, они содержат инструкции по проверке в комментариях.

Примечание: Некоторые примеры кода ссылаются на типы c_int. На платформах, где sizeof(long) == sizeof(int) является псевдонимом c_long. Таким образом, вы не должны быть сбиты с толку, если будет напечатано c_long, если вы ожидаете c_int — на самом деле они одного типа.

Доступ к функциям из загруженных библиотек dll

Доступ к функциям осуществляется как к атрибутам библиотечных объектов:

>>> libc.printf
<_FuncPtr object at 0x...>
>>> print(windll.kernel32.GetModuleHandleA)  
<_FuncPtr object at 0x...>
>>> print(windll.kernel32.MyOwnFunction)     
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "ctypes.py", line 239, in __getattr__
    func = _StdcallFuncPtr(name, self)
AttributeError: function 'MyOwnFunction' not found
>>>

Обратите внимание, что системные библиотеки dll win32, такие как kernel32 и user32, часто экспортируют версии функций как в ANSI, так и в UNICODE. Версия UNICODE экспортируется с добавлением W к имени, в то время как версия ANSI экспортируется с добавлением A к имени. Функция win32 GetModuleHandle, которая возвращает дескриптор модуля для заданного имени модуля, имеет следующий прототип на языке Си, и используется макрос, который отображает один из них как GetModuleHandle в зависимости от того, определен UNICODE или нет:

/* ANSI version */
HMODULE GetModuleHandleA(LPCSTR lpModuleName);
/* UNICODE version */
HMODULE GetModuleHandleW(LPCWSTR lpModuleName);

windll не пытается выбрать один из них волшебным образом, вы должны получить доступ к нужной вам версии, указав GetModuleHandleA или GetModuleHandleW явно, а затем вызвать его с помощью объектов bytes или string соответственно.

Иногда библиотеки dll экспортируют функции с именами, которые не являются допустимыми идентификаторами Python, например, "??2@YAPAXI@Z". В этом случае вам нужно использовать getattr() для извлечения функции:

>>> getattr(cdll.msvcrt, "??2@YAPAXI@Z")  
<_FuncPtr object at 0x...>
>>>

В Windows некоторые библиотеки dll экспортируют функции не по имени, а по порядковому номеру. Доступ к этим функциям можно получить, указав в dll-объекте порядковый номер:

>>> cdll.kernel32[1]  
<_FuncPtr object at 0x...>
>>> cdll.kernel32[0]  
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "ctypes.py", line 310, in __getitem__
    func = _StdcallFuncPtr(name, self)
AttributeError: function ordinal 0 not found
>>>

Вызывающие функции

Вы можете вызывать эти функции, как и любые другие, доступные для вызова на Python. В этом примере используется функция time(), которая возвращает системное время в секундах с эпохи Unix, и функция GetModuleHandleA(), которая возвращает дескриптор модуля win32.

В этом примере обе функции вызываются с помощью указателя NULL (в качестве указателя None следует использовать указатель NULL):

>>> print(libc.time(None))  
1150640792
>>> print(hex(windll.kernel32.GetModuleHandleA(None)))  
0x1d000000
>>>

ValueError возникает при вызове функции stdcall с использованием соглашения о вызове cdecl, или наоборот:

>>> cdll.kernel32.GetModuleHandleA(None)  
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: Procedure probably called with not enough arguments (4 bytes missing)
>>>

>>> windll.msvcrt.printf(b"spam")  
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: Procedure probably called with too many arguments (4 bytes in excess)
>>>

Чтобы узнать правильное соглашение о вызове, вам нужно заглянуть в заголовочный файл C или документацию к функции, которую вы хотите вызвать.

В Windows ctypes используется структурированная обработка исключений win32 для предотвращения сбоев из-за общих сбоев защиты, когда функции вызываются с недопустимыми значениями аргументов:

>>> windll.kernel32.GetModuleHandleA(32)  
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
OSError: exception: access violation reading 0x00000020
>>>

Однако существует достаточно способов вызвать сбой в работе Python с помощью ctypes, поэтому в любом случае вам следует быть осторожным. Модуль faulthandler может быть полезен при отладке сбоев (например, из-за ошибок сегментации, вызванных ошибочными вызовами библиотеки C).

None, целые числа, байты и строки (в юникоде) - единственные собственные объекты Python, которые могут напрямую использоваться в качестве параметров при вызове этих функций. None передается как указатель C NULL, объекты bytes и строки передаются как указатель на блок памяти, содержащий их данные (char* или wchar_t*). Целые числа Python передаются как тип C int, используемый платформами по умолчанию, их значение маскируется, чтобы соответствовать типу C.

Прежде чем мы перейдем к вызову функций с другими типами параметров, мы должны узнать больше о типах данных ctypes.

Основные типы данных

ctypes определяет ряд примитивных типов данных, совместимых с C:

типы тип

Тип C

Тип Python

c_bool

_Bool

книга (1)

c_char

char

объект размером в 1 символьный байт

c_wchar

wchar_t

строка из 1 символа

c_byte

char

инт

c_ubyte

unsigned char

инт

c_short

short

инт

c_ushort

unsigned short

инт

c_int

int

инт

c_uint

unsigned int

инт

c_long

long

инт

c_ulong

unsigned long

инт

c_longlong

__int64 или long long

инт

c_ulonglong

unsigned __int64 или unsigned long long

инт

c_size_t

size_t

инт

c_ssize_t

ssize_t или Py_ssize_t

инт

c_float

float

плыть

c_double

double

плыть

c_longdouble

long double

плыть

c_char_p

char* (нулевое значение завершено)

байты объекта или None

c_wchar_p

wchar_t* (нулевое значение завершено)

строка или None

c_void_p

void*

int или None

  1. Конструктор принимает любой объект с истинностным значением.

Все эти типы можно создать, вызвав их с необязательным инициализатором правильного типа и значения:

>>> c_int()
c_long(0)
>>> c_wchar_p("Hello, World")
c_wchar_p(140018365411392)
>>> c_ushort(-3)
c_ushort(65533)
>>>

Поскольку эти типы изменчивы, их значение также может быть изменено впоследствии:

>>> i = c_int(42)
>>> print(i)
c_long(42)
>>> print(i.value)
42
>>> i.value = -99
>>> print(i.value)
-99
>>>

Присвоение нового значения экземплярам типов указателей c_char_p, c_wchar_p, и c_void_p изменяет местоположение в памяти, на которое они указывают, а не содержимое блока памяти (конечно, нет, потому что объекты Python bytes неизменяемы).:

>>> s = "Hello, World"
>>> c_s = c_wchar_p(s)
>>> print(c_s)
c_wchar_p(139966785747344)
>>> print(c_s.value)
Hello World
>>> c_s.value = "Hi, there"
>>> print(c_s)              # the memory location has changed
c_wchar_p(139966783348904)
>>> print(c_s.value)
Hi, there
>>> print(s)                # first object is unchanged
Hello, World
>>>

Однако вам следует быть осторожным и не передавать их функциям, ожидающим указатели на изменяемую память. Если вам нужны изменяемые блоки памяти, в ctypes есть функция create_string_buffer(), которая создает их различными способами. Доступ к текущему содержимому блока памяти (или его изменение) можно получить с помощью свойства raw; если вы хотите получить доступ к нему в виде строки, заканчивающейся нулем, используйте свойство value:

>>> from ctypes import *
>>> p = create_string_buffer(3)            # create a 3 byte buffer, initialized to NUL bytes
>>> print(sizeof(p), repr(p.raw))
3 b'\x00\x00\x00'
>>> p = create_string_buffer(b"Hello")     # create a buffer containing a NUL terminated string
>>> print(sizeof(p), repr(p.raw))
6 b'Hello\x00'
>>> print(repr(p.value))
b'Hello'
>>> p = create_string_buffer(b"Hello", 10) # create a 10 byte buffer
>>> print(sizeof(p), repr(p.raw))
10 b'Hello\x00\x00\x00\x00\x00'
>>> p.value = b"Hi"
>>> print(sizeof(p), repr(p.raw))
10 b'Hi\x00lo\x00\x00\x00\x00\x00'
>>>

Функция create_string_buffer() заменяет старую функцию c_buffer() (которая все еще доступна в качестве псевдонима). Чтобы создать изменяемый блок памяти, содержащий символы юникода типа C wchar_t, используйте функцию create_unicode_buffer().

Вызывающие функции, продолжение

Обратите внимание, что printf выводит данные на реальный стандартный канал вывода, а не на sys.stdout, поэтому эти примеры будут работать только в командной строке консоли, а не из IDLE или PythonWin:

>>> printf = libc.printf
>>> printf(b"Hello, %s\n", b"World!")
Hello, World!
14
>>> printf(b"Hello, %S\n", "World!")
Hello, World!
14
>>> printf(b"%d bottles of beer\n", 42)
42 bottles of beer
19
>>> printf(b"%f bottles of beer\n", 42.5)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ArgumentError: argument 2: TypeError: Don't know how to convert parameter 2
>>>

Как упоминалось ранее, все типы объектов Python, за исключением integers, strings и bytes, должны быть обернуты в соответствующий им тип ctypes, чтобы их можно было преобразовать в требуемый тип данных C:

>>> printf(b"An int %d, a double %f\n", 1234, c_double(3.14))
An int 1234, a double 3.140000
31
>>>

Вызов переменных функций

На многих платформах вызов переменных функций с помощью ctypes в точности совпадает с вызовом функций с фиксированным числом параметров. На некоторых платформах, и в частности в ARM64 для платформ Apple, соглашение о вызове переменных функций отличается от соглашения о вызове обычных функций.

На этих платформах требуется указывать атрибут argtypes для обычных, невариантных аргументов функции:

libc.printf.argtypes = [ctypes.c_char_p]

Поскольку указание атрибута не препятствует переносимости, рекомендуется всегда указывать argtypes для всех переменных функций.

Вызов функций с вашими собственными пользовательскими типами данных

Вы также можете настроить преобразование аргументов ctypes, чтобы разрешить использовать экземпляры ваших собственных классов в качестве аргументов функции. ctypes ищет атрибут _as_parameter_ и использует его в качестве аргумента функции. Атрибут должен быть целым числом, строкой, байтами, экземпляром ctypes или объектом с атрибутом _as_parameter_:

>>> class Bottles:
...     def __init__(self, number):
...         self._as_parameter_ = number
...
>>> bottles = Bottles(42)
>>> printf(b"%d bottles of beer\n", bottles)
42 bottles of beer
19
>>>

Если вы не хотите хранить данные экземпляра в переменной экземпляра _as_parameter_, вы можете определить property, которая делает атрибут доступным по запросу.

Указание требуемых типов аргументов (прототипов функций)

Можно указать требуемые типы аргументов функций, экспортируемых из библиотек DLL, установив атрибут argtypes.

argtypes должно быть последовательностью типов данных C (функция printf, вероятно, здесь не является хорошим примером, поскольку она принимает переменное число и различные типы параметров в зависимости от строки формата, с другой стороны, это довольно удобно для экспериментов с помощью этой функции):

>>> printf.argtypes = [c_char_p, c_char_p, c_int, c_double]
>>> printf(b"String '%s', Int %d, Double %f\n", b"Hi", 10, 2.2)
String 'Hi', Int 10, Double 2.200000
37
>>>

Указание формата защищает от несовместимых типов аргументов (как прототип для функции C) и пытается преобразовать аргументы в допустимые типы:

>>> printf(b"%d %d %d", 1, 2, 3)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ArgumentError: argument 2: TypeError: wrong type
>>> printf(b"%s %d %f\n", b"X", 2, 3)
X 2 3.000000
13
>>>

Если вы определили свои собственные классы, которые вы передаете вызовам функций, вам необходимо реализовать метод класса from_param(), чтобы они могли использовать их в последовательности argtypes. Метод класса from_param() получает объект Python, переданный в вызов функции, он должен выполнить проверку типа или что-то еще, что необходимо, чтобы убедиться, что этот объект приемлем, а затем вернуть сам объект, его атрибут _as_parameter_ или все, что вы хотите передать в данном случае в качестве аргумента функции C . Опять же, результатом должно быть целое число, строка, байты, экземпляр ctypes или объект с атрибутом _as_parameter_.

Типы возвращаемых данных

По умолчанию предполагается, что функции возвращают тип C int. Другие типы возвращаемых данных можно задать, установив атрибут restype для объекта function.

Вот более продвинутый пример, в нем используется функция strchr, которая ожидает указатель на строку и символ и возвращает указатель на строку:

>>> strchr = libc.strchr
>>> strchr(b"abcdef", ord("d"))  
8059983
>>> strchr.restype = c_char_p    # c_char_p is a pointer to a string
>>> strchr(b"abcdef", ord("d"))
b'def'
>>> print(strchr(b"abcdef", ord("x")))
None
>>>

Если вы хотите избежать описанных выше вызовов ord("x"), вы можете установить атрибут argtypes, и второй аргумент будет преобразован из односимвольного объекта Python bytes в символ C:

>>> strchr.restype = c_char_p
>>> strchr.argtypes = [c_char_p, c_char]
>>> strchr(b"abcdef", b"d")
'def'
>>> strchr(b"abcdef", b"def")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ArgumentError: argument 2: TypeError: one character string expected
>>> print(strchr(b"abcdef", b"x"))
None
>>> strchr(b"abcdef", b"d")
'def'
>>>

Вы также можете использовать вызываемый объект Python (например, функцию или класс) в качестве атрибута restype, если внешняя функция возвращает целое число. Вызываемый объект будет вызываться с целым числом, возвращаемым функцией C, и результат этого вызова будет использоваться в качестве результата вызова вашей функции. Это полезно для проверки возвращаемых значений на наличие ошибок и автоматического создания исключения:

>>> GetModuleHandle = windll.kernel32.GetModuleHandleA  
>>> def ValidHandle(value):
...     if value == 0:
...         raise WinError()
...     return value
...
>>>
>>> GetModuleHandle.restype = ValidHandle  
>>> GetModuleHandle(None)  
486539264
>>> GetModuleHandle("something silly")  
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in ValidHandle
OSError: [Errno 126] The specified module could not be found.
>>>

WinError - это функция, которая вызывает Windows FormatMessage() api для получения строкового представления кода ошибки и возвращает исключение. WinError принимает необязательный параметр кода ошибки, если он не используется, он вызывает GetLastError(), чтобы получить его.

Пожалуйста, обратите внимание, что гораздо более мощный механизм проверки ошибок доступен с помощью атрибута errcheck; подробности смотрите в справочном руководстве.

Передача указателей (или: передача параметров по ссылке)

Иногда функция C api ожидает указатель на тип данных в качестве параметра, возможно, для записи в соответствующее местоположение, или если данные слишком велики для передачи по значению. Это также известно как передача параметров по ссылке.

ctypes экспортирует функцию byref(), которая используется для передачи параметров по ссылке. Того же эффекта можно достичь с помощью функции pointer(), хотя pointer() выполняет гораздо больше работы, поскольку создает реальный объект-указатель, поэтому быстрее использовать byref(), если вам не нужен объект-указатель в Сам Python:

>>> i = c_int()
>>> f = c_float()
>>> s = create_string_buffer(b'\000' * 32)
>>> print(i.value, f.value, repr(s.value))
0 0.0 b''
>>> libc.sscanf(b"1 3.14 Hello", b"%d %f %s",
...             byref(i), byref(f), s)
3
>>> print(i.value, f.value, repr(s.value))
1 3.1400001049 b'Hello'
>>>

Структуры и объединения

Структуры и объединения должны быть производными от базовых классов Structure и Union, которые определены в модуле ctypes. Каждый подкласс должен определять атрибут _fields_. _fields_ должен быть список из 2-х кортежей, содержащий имя поля и тип поля.

Тип поля должен быть ctypes типа, подобного c_int, или любым другим производным типом ctypes: структура, объединение, массив, указатель.

Вот простой пример точечной структуры, которая содержит два целых числа с именами 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
>>>

Однако вы можете создавать гораздо более сложные структуры. Структура сама по себе может содержать другие структуры, используя структуру в качестве типа поля.

Вот прямоугольная структура, которая содержит две точки с именами 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))

Поля descriptor можно извлечь из class, они полезны для отладки, поскольку могут содержать полезную информацию:

>>> print(POINT.x)
<Field type=c_long, ofs=0, size=4>
>>> print(POINT.y)
<Field type=c_long, ofs=4, size=4>
>>>

Предупреждение

ctypes не поддерживает передачу объединений или структур с битовыми полями в функции по значению. Хотя это может работать на 32-разрядной платформе x86, библиотека не гарантирует, что это сработает в общем случае. Объединения и структуры с битовыми полями всегда должны передаваться функциям по указателю.

Выравнивание структуры/объединения и порядок байтов

По умолчанию поля Structure и Union выровнены таким же образом, как это делает компилятор C. Можно переопределить это поведение, указав атрибут _pack_ class в определении подкласса. Это значение должно быть положительным целым числом и указывать максимальное выравнивание полей. Это то, что #pragma pack(n) также выполняется в MSVC.

ctypes использует собственный порядок байтов для структур и объединений. Для создания структур с нестандартным порядком байтов вы можете использовать один из базовых классов BigEndianStructure, LittleEndianStructure, BigEndianUnion, и LittleEndianUnion. Эти классы не могут содержать поля указателей.

Битовые поля в структурах и объединениях

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

Массивы

Массивы - это последовательности, содержащие фиксированное количество экземпляров одного и того же типа.

Рекомендуемый способ создания типов массивов - это умножение типа данных на положительное целое число:

TenPointsArrayType = POINT * 10

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

>>> from ctypes import *
>>> class POINT(Structure):
...     _fields_ = ("x", c_int), ("y", c_int)
...
>>> class MyStruct(Structure):
...     _fields_ = [("a", c_int),
...                 ("b", c_float),
...                 ("point_array", POINT * 4)]
>>>
>>> print(len(MyStruct().point_array))
4
>>>

Экземпляры создаются обычным способом, путем вызова класса:

arr = TenPointsArrayType()
for pt in arr:
    print(pt.x, pt.y)

Приведенный выше код выводит серию строк 0 0, поскольку содержимое массива инициализируется нулями.

Также могут быть указаны инициализаторы правильного типа:

>>> from ctypes import *
>>> TenIntegers = c_int * 10
>>> ii = TenIntegers(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
>>> print(ii)
<c_long_Array_10 object at 0x...>
>>> for i in ii: print(i, end=" ")
...
1 2 3 4 5 6 7 8 9 10
>>>

Указатели

Экземпляры указателей создаются путем вызова функции pointer() для типа ctypes:

>>> from ctypes import *
>>> i = c_int(42)
>>> pi = pointer(i)
>>>

Экземпляры указателя имеют атрибут contents, который возвращает объект, на который указывает указатель, объект i, указанный выше:

>>> pi.contents
c_long(42)
>>>

Обратите внимание, что ctypes не возвращает значение OUR (исходный объект), он создает новый эквивалентный объект каждый раз, когда вы извлекаете атрибут:

>>> pi.contents is i
False
>>> pi.contents is pi.contents
False
>>>

Присвоение другого экземпляра c_int атрибуту содержимого указателя приведет к тому, что указатель будет указывать на область памяти, где он хранится:

>>> i = c_int(99)
>>> pi.contents = i
>>> pi.contents
c_long(99)
>>>

Экземпляры указателей также могут быть проиндексированы целыми числами:

>>> pi[0]
99
>>>

Присвоение целочисленному индексу изменяет указанное значение:

>>> print(i)
c_long(99)
>>> pi[0] = 22
>>> print(i)
c_long(22)
>>>

Также возможно использовать индексы, отличные от 0, но вы должны знать, что делаете, точно так же, как в C: вы можете получить доступ к произвольным ячейкам памяти или изменить их. Как правило, вы используете эту функцию только в том случае, если получаете указатель из функции C и знаете, что указатель на самом деле указывает на массив, а не на отдельный элемент.

За кулисами функция pointer() делает больше, чем просто создает экземпляры указателей, она должна сначала создать типы указателей. Это делается с помощью функции POINTER(), которая принимает любой тип ctypes и возвращает новый тип:

>>> PI = POINTER(c_int)
>>> PI
<class 'ctypes.LP_c_long'>
>>> PI(42)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: expected c_long instead of int
>>> PI(c_int(42))
<ctypes.LP_c_long object at 0x...>
>>>

Вызов типа pointer без аргумента создает указатель NULL. NULL Указатели имеют логическое значение False:

>>> null_ptr = POINTER(c_int)()
>>> print(bool(null_ptr))
False
>>>

ctypes проверяет наличие NULL при разыменовании указателей (но разыменование недопустимых указателей, отличных от ``NULL``, приведет к сбою Python):

>>> null_ptr[0]
Traceback (most recent call last):
    ....
ValueError: NULL pointer access
>>>

>>> null_ptr[0] = 1234
Traceback (most recent call last):
    ....
ValueError: NULL pointer access
>>>

Преобразования типов

Обычно ctypes выполняет строгую проверку типов. Это означает, что если у вас есть POINTER(c_int) в списке argtypes функции или в качестве типа поля-члена в определении структуры, принимаются только экземпляры точно такого же типа. Из этого правила есть несколько исключений, когда ctypes принимает другие объекты. Например, вы можете передавать совместимые экземпляры массива вместо типов указателей. Таким образом, для POINTER(c_int) types принимает массив c_int:

>>> class Bar(Structure):
...     _fields_ = [("count", c_int), ("values", POINTER(c_int))]
...
>>> bar = Bar()
>>> bar.values = (c_int * 3)(1, 2, 3)
>>> bar.count = 3
>>> for i in range(bar.count):
...     print(bar.values[i])
...
1
2
3
>>>

Кроме того, если аргумент функции явно объявлен как тип указателя (например, POINTER(c_int)) в argtypes, в функцию может быть передан объект указанного типа (в данном случае c_int). в этом случае ctypes автоматически применит требуемое преобразование byref().

Чтобы задать полю типа УКАЗАТЕЛЯ значение NULL, вы можете назначить None:

>>> bar.values = None
>>>

Иногда встречаются экземпляры несовместимых типов. В C можно преобразовать один тип в другой. ctypes предоставляет функцию cast(), которая может использоваться аналогичным образом. Структура Bar, определенная выше, принимает POINTER(c_int) указатели или c_int массивы для своего поля values, но не экземпляры других типов:

>>> bar.values = (c_byte * 4)()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: incompatible types, c_byte_Array_4 instance instead of LP_c_long instance
>>>

Для таких случаев удобна функция cast().

Функция cast() может использоваться для преобразования экземпляра ctypes в указатель на другой тип данных ctypes. cast() принимает два параметра: объект ctypes, который является или может быть преобразован в какой-либо указатель, и тип указателя ctypes. Возвращает экземпляр второго аргумента, который ссылается на тот же блок памяти, что и первый аргумент:

>>> a = (c_byte * 4)()
>>> cast(a, POINTER(c_int))
<ctypes.LP_c_long object at ...>
>>>

Таким образом, cast() можно использовать для присвоения values полю Bar структуры:

>>> bar = Bar()
>>> bar.values = cast((c_byte * 4)(), POINTER(c_int))
>>> print(bar.values[0])
0
>>>

Неполные типы

Неполные типы - это структуры, объединения или массивы, члены которых еще не определены. В C они задаются с помощью прямых объявлений, которые будут определены позже:

struct cell; /* forward declaration */

struct cell {
    char *name;
    struct cell *next;
};

Простой перевод в код ctypes был бы таким, но это не работает:

>>> class cell(Structure):
...     _fields_ = [("name", c_char_p),
...                 ("next", POINTER(cell))]
...
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in cell
NameError: name 'cell' is not defined
>>>

потому что новый class cell недоступен в самой инструкции class. В ctypes мы можем определить класс cell и установить атрибут _fields_ позже, после инструкции class:

>>> from ctypes import *
>>> class cell(Structure):
...     pass
...
>>> cell._fields_ = [("name", c_char_p),
...                  ("next", POINTER(cell))]
>>>

Давайте попробуем. Мы создаем два экземпляра cell и позволяем им указывать друг на друга, а затем несколько раз проходим по цепочке указателей:

>>> c1 = cell()
>>> c1.name = b"foo"
>>> c2 = cell()
>>> c2.name = b"bar"
>>> c1.next = pointer(c2)
>>> c2.next = pointer(c1)
>>> p = c1
>>> for i in range(8):
...     print(p.name, end=" ")
...     p = p.next[0]
...
foo bar foo bar foo bar foo bar
>>>

Функции обратного вызова

ctypes позволяет создавать указатели на вызываемые функции C из вызываемых объектов Python. Их иногда называют функциями обратного вызова.

Во-первых, вы должны создать класс для функции обратного вызова. Класс знает соглашение о вызове, тип возвращаемого значения, а также количество и типы аргументов, которые будет принимать эта функция.

Фабричная функция CFUNCTYPE() создает типы для функций обратного вызова, используя соглашение о вызове cdecl. В Windows фабричная функция WINFUNCTYPE() создает типы для функций обратного вызова, используя соглашение о вызове stdcall.

Обе эти фабричные функции вызываются с типом результата в качестве первого аргумента, а функции обратного вызова используют ожидаемые типы аргументов в качестве остальных аргументов.

Здесь я приведу пример, в котором используется функция стандартной библиотеки C qsort(), которая используется для сортировки элементов с помощью функции обратного вызова. qsort() будет использоваться для сортировки массива целых чисел:

>>> IntArray5 = c_int * 5
>>> ia = IntArray5(5, 1, 7, 33, 99)
>>> qsort = libc.qsort
>>> qsort.restype = None
>>>

qsort() должен вызываться с указателем на данные для сортировки, количество элементов в массиве данных, размер одного элемента и указатель на функцию сравнения, обратный вызов. Затем будет вызван обратный вызов с двумя указателями на элементы, и он должен вернуть отрицательное целое число, если первый элемент меньше второго, ноль, если они равны, и положительное целое число в противном случае.

Таким образом, наша функция обратного вызова получает указатели на целые числа и должна возвращать целое число. Сначала мы создаем type для функции обратного вызова:

>>> CMPFUNC = CFUNCTYPE(c_int, POINTER(c_int), POINTER(c_int))
>>>

Для начала, вот простой обратный вызов, который показывает передаваемые значения:

>>> def py_cmp_func(a, b):
...     print("py_cmp_func", a[0], b[0])
...     return 0
...
>>> cmp_func = CMPFUNC(py_cmp_func)
>>>

Результат:

>>> qsort(ia, len(ia), sizeof(c_int), cmp_func)  
py_cmp_func 5 1
py_cmp_func 33 99
py_cmp_func 7 33
py_cmp_func 5 7
py_cmp_func 1 7
>>>

Теперь мы действительно можем сравнить эти два элемента и получить полезный результат:

>>> def py_cmp_func(a, b):
...     print("py_cmp_func", a[0], b[0])
...     return a[0] - b[0]
...
>>>
>>> qsort(ia, len(ia), sizeof(c_int), CMPFUNC(py_cmp_func)) 
py_cmp_func 5 1
py_cmp_func 33 99
py_cmp_func 7 33
py_cmp_func 1 7
py_cmp_func 5 7
>>>

Как мы можем легко проверить, теперь наш массив отсортирован:

>>> for i in ia: print(i, end=" ")
...
1 5 7 33 99
>>>

Фабрики функций можно использовать в качестве фабрик декораторов, поэтому мы также можем написать:

>>> @CFUNCTYPE(c_int, POINTER(c_int), POINTER(c_int))
... def py_cmp_func(a, b):
...     print("py_cmp_func", a[0], b[0])
...     return a[0] - b[0]
...
>>> qsort(ia, len(ia), sizeof(c_int), py_cmp_func)
py_cmp_func 5 1
py_cmp_func 33 99
py_cmp_func 7 33
py_cmp_func 1 7
py_cmp_func 5 7
>>>

Примечание

Убедитесь, что вы сохраняете ссылки на объекты CFUNCTYPE() до тех пор, пока они используются из кода на C. ctypes этого не делает, и если вы этого не сделаете, они могут быть собраны мусором, что приведет к сбою вашей программы при выполнении обратного вызова.

Также обратите внимание, что если функция обратного вызова вызывается в потоке, созданном вне контроля Python (например, с помощью внешнего кода, который вызывает обратный вызов), ctypes создает новый фиктивный поток Python при каждом вызове. Такое поведение корректно для большинства целей, но это означает, что значения, сохраненные с помощью threading.local, не будут сохраняться при разных обратных вызовах, даже если эти вызовы выполняются из одного и того же потока C.

Доступ к значениям, экспортированным из библиотек dll

Некоторые совместно используемые библиотеки экспортируют не только функции, но и переменные. Примером в самой библиотеке Python является Py_OptimizeFlag, целое число, равное 0, 1 или 2, в зависимости от флага -O или -OO, заданного при запуске.

ctypes может получать доступ к таким значениям с помощью методов класса in_dll() типа. pythonapi - это предопределенный символ, предоставляющий доступ к api Python C:

>>> opt_flag = c_int.in_dll(pythonapi, "Py_OptimizeFlag")
>>> print(opt_flag)
c_long(0)
>>>

Если бы интерпретатор был запущен с -O, в примере было бы напечатано c_long(1) или c_long(2), если бы было указано -OO.

Расширенный пример, который также демонстрирует использование указателей, обращается к указателю PyImport_FrozenModules, экспортированному Python.

Цитирую документы для этого значения:

Этот указатель инициализируется как указывающий на массив записей _frozen, заканчивающийся записью, все элементы которой равны NULL или нулю. При импорте замороженного модуля выполняется поиск по нему в этой таблице. Сторонний код мог бы сыграть с этим злую шутку, предоставив динамически создаваемую коллекцию замороженных модулей.

Так что манипулирование этим указателем может оказаться даже полезным. Чтобы ограничить размер примера, мы покажем только, как эту таблицу можно прочитать с помощью ctypes:

>>> from ctypes import *
>>>
>>> class struct_frozen(Structure):
...     _fields_ = [("name", c_char_p),
...                 ("code", POINTER(c_ubyte)),
...                 ("size", c_int),
...                 ("get_code", POINTER(c_ubyte)),  # Function pointer
...                ]
...
>>>

Мы определили тип данных _frozen, чтобы мы могли получить указатель на таблицу:

>>> FrozenTable = POINTER(struct_frozen)
>>> table = FrozenTable.in_dll(pythonapi, "_PyImport_FrozenBootstrap")
>>>

Поскольку table является pointer для массива записей struct_frozen, мы можем выполнить итерацию по нему, но мы просто должны убедиться, что наш цикл завершается, потому что указатели не имеют размера. Рано или поздно это, вероятно, приведет к сбою из-за нарушения прав доступа или чего-то еще, поэтому лучше выйти из цикла, когда мы нажмем на запись NULL:

>>> for item in table:
...     if item.name is None:
...         break
...     print(item.name.decode("ascii"), item.size)
...
_frozen_importlib 31764
_frozen_importlib_external 41499
zipimport 12345
>>>

Тот факт, что в стандартном Python есть замороженный модуль и замороженный пакет (обозначенный отрицательным элементом size), не очень известен, он используется только для тестирования. Попробуйте, например, с помощью import __hello__.

Сюрпризы

В 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
>>> # now swap the two points
>>> 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 не дало ожидаемого эффекта.

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

Другой пример, который может вести себя не так, как можно было бы ожидать, заключается в следующем:

>>> s = c_char_p()
>>> s.value = b"abc def ghi"
>>> s.value
b'abc def ghi'
>>> s.value is s.value
False
>>>

Примечание

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

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

Типы данных переменного размера

ctypes обеспечивает некоторую поддержку массивов и структур переменного размера.

Функция resize() может использоваться для изменения размера буфера памяти существующего объекта ctypes. Функция принимает объект в качестве первого аргумента и запрашиваемый размер в байтах в качестве второго аргумента. Размер блока памяти не может быть меньше размера естественного блока памяти, указанного типом objects, при попытке выполнения этого действия генерируется значение ValueError:

>>> short_array = (c_short * 4)()
>>> print(sizeof(short_array))
8
>>> resize(short_array, 4)
Traceback (most recent call last):
    ...
ValueError: minimum size is 8
>>> resize(short_array, 32)
>>> sizeof(short_array)
32
>>> sizeof(type(short_array))
8
>>>

Это хорошо, но как можно получить доступ к дополнительным элементам, содержащимся в этом массиве? Поскольку type по-прежнему знает только о 4 элементах, мы получаем ошибки при доступе к другим элементам:

>>> short_array[:]
[0, 0, 0, 0]
>>> short_array[7]
Traceback (most recent call last):
    ...
IndexError: invalid index
>>>

Другой способ использовать типы данных переменного размера с ctypes - это использовать динамическую природу Python и (повторно) определять тип данных после того, как требуемый размер уже известен, в каждом конкретном случае.

ссылка на ctypes

Поиск общих библиотек

При программировании на компилируемом языке доступ к общим библиотекам осуществляется при компиляции/компоновке программы и при запуске программы.

Назначение функции find_library() состоит в том, чтобы найти библиотеку способом, аналогичным тому, который выполняет компилятор или загрузчик среды выполнения (на платформах с несколькими версиями общей библиотеки должна быть загружена самая последняя), в то время как загрузчики библиотек ctypes действуют так же, как при запуске программы, и вызовите загрузчик среды выполнения напрямую.

Модуль ctypes.util предоставляет функцию, которая может помочь определить библиотеку для загрузки.

ctypes.util.find_library(name)

Попробуйте найти библиотеку и вернуть путь к ней. name - это имя библиотеки без какого-либо префикса типа lib, суффикса типа .so, .dylib или номера версии (это форма, используемая для параметра компоновщика posix -l). Если библиотека не найдена, возвращает None.

Точная функциональность зависит от системы.

В Linux find_library() пытается запустить внешние программы (/sbin/ldconfig, gcc, objdump и ld), чтобы найти файл библиотеки. Он возвращает имя файла библиотеки.

Изменено в версии 3.6: В Linux значение переменной окружения LD_LIBRARY_PATH используется при поиске библиотек, если библиотека не может быть найдена никакими другими способами.

Вот несколько примеров:

>>> from ctypes.util import find_library
>>> find_library("m")
'libm.so.6'
>>> find_library("c")
'libc.so.6'
>>> find_library("bz2")
'libbz2.so.1.0'
>>>

В macOS find_library() пробует несколько предопределенных схем именования и путей для поиска библиотеки и в случае успеха возвращает полный путь:

>>> from ctypes.util import find_library
>>> find_library("c")
'/usr/lib/libc.dylib'
>>> find_library("m")
'/usr/lib/libm.dylib'
>>> find_library("bz2")
'/usr/lib/libbz2.dylib'
>>> find_library("AGL")
'/System/Library/Frameworks/AGL.framework/AGL'
>>>

В Windows find_library() выполняет поиск по пути системного поиска и возвращает полный путь, но поскольку нет предопределенной схемы именования, вызов типа find_library("c") завершится ошибкой и вернет None.

При обертывании общей библиотеки с помощью ctypes, возможно, было бы лучше определить имя общей библиотеки во время разработки и жестко запрограммировать его в модуле-оболочке вместо использования find_library() для определения местоположения библиотеки во время выполнения.

Загрузка разделяемых библиотек

Существует несколько способов загрузки разделяемых библиотек в процесс Python. Одним из способов является создание экземпляра одного из следующих классов:

class ctypes.CDLL(name, mode=DEFAULT_MODE, handle=None, use_errno=False, use_last_error=False, winmode=None)

Экземпляры этого класса представляют собой загруженные разделяемые библиотеки. Функции в этих библиотеках используют стандартное соглашение о вызове C и, как предполагается, возвращают int.

В Windows создание экземпляра CDLL может завершиться ошибкой, даже если имя библиотеки DLL существует. Когда зависимая библиотека DLL загруженной библиотеки DLL не найдена, возникает ошибка OSError с сообщением «[WinError 126] Указанный модуль не найден». Это сообщение об ошибке не содержит имени отсутствующей библиотеки DLL, поскольку Windows API не возвращает эту информацию, что затрудняет диагностику этой ошибки. Чтобы устранить эту ошибку и определить, какая библиотека DLL не найдена, вам необходимо найти список зависимых библиотек DLL и определить, какая из них не найдена, с помощью средств отладки и трассировки Windows.

См.также

Microsoft DUMPBIN tool - Инструмент для поиска зависимых библиотек DLL.

class ctypes.OleDLL(name, mode=DEFAULT_MODE, handle=None, use_errno=False, use_last_error=False, winmode=None)

Только для Windows: Экземпляры этого класса представляют загруженные общие библиотеки, функции в этих библиотеках используют соглашение о вызове stdcall и, как предполагается, возвращают специфичный для Windows код HRESULT. Значения HRESULT содержат информацию, указывающую, завершился ли вызов функции неудачно или успешно, а также дополнительный код ошибки. Если возвращаемое значение сигнализирует о сбое, автоматически генерируется OSError.

Изменено в версии 3.3: раньше был поднят WindowsError, который теперь является псевдонимом OSError.

class ctypes.WinDLL(name, mode=DEFAULT_MODE, handle=None, use_errno=False, use_last_error=False, winmode=None)

Только для Windows: Экземпляры этого класса представляют загруженные общие библиотеки, функции в этих библиотеках используют соглашение о вызове stdcall и, как предполагается, возвращают int по умолчанию.

Код Python global interpreter lock освобождается перед вызовом любой функции, экспортируемой этими библиотеками, и повторно запрашивается после этого.

class ctypes.PyDLL(name, mode=DEFAULT_MODE, handle=None)

Экземпляры этого класса ведут себя как экземпляры CDLL, за исключением того, что GIL Python не освобождается во время вызова функции, а после выполнения функции проверяется флаг ошибки Python. Если флаг ошибки установлен, генерируется исключение Python.

Таким образом, это полезно только для прямого вызова функций Python C api.

Все эти классы можно создать, вызвав их по крайней мере с одним аргументом - путем к общей библиотеке. Если у вас есть существующий дескриптор для уже загруженной общей библиотеки, он может быть передан как именованный параметр handle, в противном случае для загрузки библиотеки в процесс используется функция базовых платформ dlopen или LoadLibrary, и чтобы разобраться с этим.

Параметр mode можно использовать для указания способа загрузки библиотеки. Для получения дополнительной информации обратитесь к справочной странице dlopen(3). В Windows параметр mode игнорируется. В системах posix RTLD_NOW всегда добавляется и не настраивается.

Параметр use_errno, если ему присвоено значение true, включает механизм ctypes, который позволяет безопасно получить доступ к системному номеру ошибки errno. ctypes поддерживает локальную копию системной переменной errno; если вы вызываете внешние функции, созданные с помощью use_errno=True, затем значение errno перед вызовом функции заменяется частной копией ctypes, то же самое происходит сразу после вызова функции.

Функция ctypes.get_errno() возвращает значение частной копии ctypes, а функция ctypes.set_errno() изменяет личную копию ctypes на новое значение и возвращает прежнее значение.

Параметр use_last_error, если ему присвоено значение true, включает тот же механизм для кода ошибки Windows, который управляется функциями GetLastError() и SetLastError() Windows API; ctypes.get_last_error() и ctypes.set_last_error() являются используется для запроса и изменения личной копии кода ошибки Windows в ctypes.

Параметр winmode используется в Windows для указания способа загрузки библиотеки (поскольку параметр mode игнорируется). Он принимает любое значение, допустимое для параметра Win32 API LoadLibraryEx flags. Если этот параметр опущен, по умолчанию используются флаги, которые обеспечивают наиболее безопасную загрузку библиотеки DLL, чтобы избежать таких проблем, как перехват библиотеки DLL. Передача полного пути к библиотеке DLL - это самый безопасный способ обеспечить правильную загрузку библиотеки и зависимостей.

Изменено в версии 3.8: Добавлен параметр winmode.

ctypes.RTLD_GLOBAL

Флаг для использования в качестве параметра mode. На платформах, где этот флаг недоступен, он определяется как целое число, равное нулю.

ctypes.RTLD_LOCAL

Флаг для использования в качестве параметра mode. На платформах, где этот параметр недоступен, он используется как RTLD_GLOBAL.

ctypes.DEFAULT_MODE

Режим по умолчанию, который используется для загрузки общих библиотек. В OSX 10.3 это RTLD_GLOBAL, в противном случае это то же самое, что RTLD_LOCAL.

Экземпляры этих классов не имеют общедоступных методов. К функциям, экспортируемым из общей библиотеки, можно обращаться как к атрибутам или по индексу. Обратите внимание, что при обращении к функции через атрибут кэшируется результат, и, следовательно, при повторном обращении к ней каждый раз возвращается один и тот же объект. С другой стороны, доступ к нему через индекс каждый раз возвращает новый объект:

>>> from ctypes import CDLL
>>> libc = CDLL("libc.so.6")  # On Linux
>>> libc.time == libc.time
True
>>> libc['time'] == libc['time']
False

Доступны следующие общедоступные атрибуты, их названия начинаются с символа подчеркивания, чтобы не противоречить именам экспортируемых функций:

PyDLL._handle

Системный дескриптор, используемый для доступа к библиотеке.

PyDLL._name

Имя библиотеки, переданное в конструкторе.

Общие библиотеки также могут быть загружены с помощью одного из готовых объектов, которые являются экземплярами класса LibraryLoader, либо путем вызова метода LoadLibrary(), либо путем извлечения библиотеки в качестве атрибута экземпляра загрузчика.

class ctypes.LibraryLoader(dlltype)

Класс, который загружает разделяемые библиотеки. dlltype должен быть одним из типов CDLL, PyDLL, WinDLL, или OleDLL.

__getattr__() имеет особое поведение: позволяет загружать общую библиотеку, обращаясь к ней как к атрибуту экземпляра загрузчика библиотеки. Результат кэшируется, поэтому при повторном обращении к атрибуту каждый раз возвращается одна и та же библиотека.

LoadLibrary(name)

Загрузите в процесс разделяемую библиотеку и верните ее. Этот метод всегда возвращает новый экземпляр библиотеки.

Доступны эти сборные загрузчики библиотек:

ctypes.cdll

Создает экземпляры CDLL.

ctypes.windll

Только для Windows: Создает WinDLL экземпляров.

ctypes.oledll

Только для Windows: Создает OleDLL экземпляров.

ctypes.pydll

Создает экземпляры PyDLL.

Для прямого доступа к api Python доступен готовый к использованию объект общей библиотеки Python:

ctypes.pythonapi

Экземпляр PyDLL, который предоставляет функции API Python C в качестве атрибутов. Обратите внимание, что предполагается, что все эти функции возвращают C int, что, конечно, не всегда соответствует действительности, поэтому вам необходимо назначить правильный атрибут restype для использования этих функций.

Создает auditing event ctypes.dlopen с аргументом name.

Создает auditing event ctypes.dlsym с аргументами library, name.

Создает auditing event ctypes.dlsym/handle с аргументами handle, name.

Иностранные функции

Как объяснялось в предыдущем разделе, к внешним функциям можно обращаться как к атрибутам загруженных разделяемых библиотек. Созданные таким образом функциональные объекты по умолчанию принимают любое количество аргументов, принимают в качестве аргументов экземпляры данных любых типов и возвращают тип результата по умолчанию, указанный загрузчиком библиотеки. Они являются экземплярами закрытого класса:

class ctypes._FuncPtr

Базовый класс для внешних функций, вызываемых на языке Си.

Экземпляры внешних функций также являются типами данных, совместимыми с C; они представляют собой указатели на функции C.

Это поведение можно настроить, назначив специальные атрибуты объекту внешней функции.

restype

Назначьте тип ctypes, чтобы указать тип результата внешней функции. Используйте None для void, функции, которая ничего не возвращает.

Можно назначить вызываемый объект Python, который не является типом ctypes, в этом случае предполагается, что функция возвращает C int, и вызываемый объект будет вызываться с этим целым числом, что позволяет выполнять дальнейшую обработку или проверку ошибок. Использование этого параметра не рекомендуется, для более гибкой последующей обработки или проверки ошибок используйте тип данных ctypes как restype и назначьте вызываемый атрибут атрибуту errcheck.

argtypes

Назначьте набор типов types, чтобы указать типы аргументов, которые принимает функция. Функции, использующие соглашение о вызове stdcall, могут быть вызваны только с тем количеством аргументов, которое равно длине этого кортежа; функции, использующие соглашение о вызове C, также принимают дополнительные, неуказанные аргументы.

Когда вызывается внешняя функция, каждый фактический аргумент передается методу класса from_param() элементов в кортеже argtypes, этот метод позволяет адаптировать фактический аргумент к объекту, который принимает внешняя функция. Например, элемент c_char_p в кортеже argtypes преобразует строку, переданную в качестве аргумента, в объект bytes, используя правила преобразования ctypes.

Новое: Теперь можно помещать элементы в argtypes, которые не являются типами ctypes, но у каждого элемента должен быть метод from_param(), который возвращает значение, используемое в качестве аргумента (целое число, строка, экземпляр ctypes). Это позволяет определить адаптеры, которые могут адаптировать пользовательские объекты в качестве функциональных параметров.

errcheck

Присвойте этому атрибуту функцию Python или другой вызываемый объект. Вызываемый объект будет вызываться с тремя или более аргументами:

callable(result, func, arguments)

result - это то, что возвращает внешняя функция, как указано в атрибуте restype.

func - это сам внешний функциональный объект, который позволяет повторно использовать один и тот же вызываемый объект для проверки или последующей обработки результатов нескольких функций.

arguments - это кортеж, содержащий параметры, первоначально переданные при вызове функции, что позволяет специализировать поведение на используемых аргументах.

Объект, возвращаемый этой функцией, будет возвращен из вызова внешней функции, но он также может проверить результирующее значение и вызвать исключение, если вызов внешней функции завершился неудачей.

exception ctypes.ArgumentError

Это исключение возникает, когда вызов внешней функции не может преобразовать один из переданных аргументов.

Создает auditing event ctypes.seh_exception с аргументом code.

Создает auditing event ctypes.call_function с аргументами func_pointer, arguments.

Прототипы функций

Внешние функции также могут быть созданы путем создания экземпляров прототипов функций. Прототипы функций аналогичны прототипам функций в C; они описывают функцию (тип возвращаемого значения, типы аргументов, соглашение о вызове) без определения реализации. Фабричные функции должны вызываться с требуемым типом результата и типами аргументов функции и могут использоваться в качестве фабрик-декораторов и, как таковые, применяться к функциям с помощью синтаксиса @wrapper. Примеры приведены в Функции обратного вызова.

ctypes.CFUNCTYPE(restype, *argtypes, use_errno=False, use_last_error=False)

Возвращаемый прототип функции создает функции, которые используют стандартное соглашение о вызове C. Функция освободит GIL во время вызова. Если для параметра use_errno установлено значение true, личная копия системной переменной errno заменяется на реальное значение errno до и после вызова; use_last_error делает то же самое для кода ошибки Windows.

ctypes.WINFUNCTYPE(restype, *argtypes, use_errno=False, use_last_error=False)

Только для Windows: Возвращаемый прототип функции создает функции, которые используют соглашение о вызове stdcall. Функция освободит GIL во время вызова. use_errno и use_last_error имеют то же значение, что и выше.

ctypes.PYFUNCTYPE(restype, *argtypes)

Возвращаемый прототип функции создает функции, которые используют соглашение о вызове Python. Функция не будет * освобождать GIL во время вызова.

Прототипы функций, созданные этими фабричными функциями, могут быть созданы различными способами, в зависимости от типа и количества параметров в вызове:

prototype(address)

Возвращает внешнюю функцию по указанному адресу, который должен быть целым числом.

prototype(callable)

Создайте вызываемую функцию C (функцию обратного вызова) из вызываемой функции Python.

prototype(func_spec[, paramflags])

Возвращает внешнюю функцию, экспортированную из общей библиотеки. func_spec должен содержать 2 кортежа (name_or_ordinal, library). Первый элемент - это имя экспортируемой функции в виде строки или порядковый номер экспортируемой функции в виде небольшого целого числа. Второй элемент - это экземпляр общей библиотеки.

prototype(vtbl_index, name[, paramflags[, iid]])

Возвращает внешнюю функцию, которая вызовет COM-метод. vtbl_index - это индекс в таблице виртуальной функции, небольшое неотрицательное целое число. name - это имя COM-метода. iid - это необязательный указатель на идентификатор интерфейса, который используется в расширенных отчетах об ошибках.

COM-методы используют специальное соглашение о вызове: они требуют указатель на COM-интерфейс в качестве первого аргумента в дополнение к тем параметрам, которые указаны в кортеже argtypes.

Необязательный параметр paramflags создает внешние оболочки функций с гораздо большей функциональностью, чем функции, описанные выше.

paramflags должен быть кортежем той же длины, что и argtypes.

Каждый элемент в этом кортеже содержит дополнительную информацию о параметре, это должен быть кортеж, содержащий один, два или три элемента.

Первый элемент - это целое число, содержащее комбинацию флагов направления для параметра:

1

Задает входной параметр для функции.

2

Выходной параметр. Внешняя функция вводит значение.

4

Входной параметр, значение которого по умолчанию равно целочисленному нулю.

Вторым необязательным элементом является имя параметра в виде строки. Если это указано, внешняя функция может быть вызвана с именованными параметрами.

Необязательный третий элемент является значением по умолчанию для этого параметра.

В следующем примере показано, как обернуть функцию Windows MessageBoxW таким образом, чтобы она поддерживала параметры по умолчанию и именованные аргументы. Объявление на языке C из заголовочного файла Windows выглядит следующим образом:

WINUSERAPI int WINAPI
MessageBoxW(
    HWND hWnd,
    LPCWSTR lpText,
    LPCWSTR lpCaption,
    UINT uType);

Вот обертывание с помощью ctypes:

>>> from ctypes import c_int, WINFUNCTYPE, windll
>>> from ctypes.wintypes import HWND, LPCWSTR, UINT
>>> prototype = WINFUNCTYPE(c_int, HWND, LPCWSTR, LPCWSTR, UINT)
>>> paramflags = (1, "hwnd", 0), (1, "text", "Hi"), (1, "caption", "Hello from ctypes"), (1, "flags", 0)
>>> MessageBox = prototype(("MessageBoxW", windll.user32), paramflags)

Внешняя функция MessageBox теперь может быть вызвана следующими способами:

>>> MessageBox()
>>> MessageBox(text="Spam, spam, spam")
>>> MessageBox(flags=2, text="foo bar")

Второй пример демонстрирует выходные параметры. Функция win32 GetWindowRect извлекает размеры указанного окна, копируя их в структуру RECT, которую должен предоставить вызывающий объект. Вот объявление на языке Си:

WINUSERAPI BOOL WINAPI
GetWindowRect(
     HWND hWnd,
     LPRECT lpRect);

Вот обертывание с помощью ctypes:

>>> from ctypes import POINTER, WINFUNCTYPE, windll, WinError
>>> from ctypes.wintypes import BOOL, HWND, RECT
>>> prototype = WINFUNCTYPE(BOOL, HWND, POINTER(RECT))
>>> paramflags = (1, "hwnd"), (2, "lprect")
>>> GetWindowRect = prototype(("GetWindowRect", windll.user32), paramflags)
>>>

Функции с выходными параметрами будут автоматически возвращать значение выходного параметра, если таковое имеется, или кортеж, содержащий значения выходных параметров, если их несколько, поэтому функция GetWindowRect теперь возвращает экземпляр RECT при вызове.

Выходные параметры могут быть объединены с протоколом errcheck для дальнейшей обработки выходных данных и проверки ошибок. Функция win32 GetWindowRect api возвращает значение BOOL, сигнализирующее об успехе или неудаче, поэтому эта функция может выполнять проверку ошибок и генерировать исключение при сбое вызова api:

>>> def errcheck(result, func, args):
...     if not result:
...         raise WinError()
...     return args
...
>>> GetWindowRect.errcheck = errcheck
>>>

Если функция errcheck возвращает полученный ею кортеж аргументов без изменений, ctypes продолжает обычную обработку выходных параметров. Если вы хотите вернуть набор координат окна вместо экземпляра RECT, вы можете извлечь поля из функции и вернуть их вместо этого, обычная обработка больше не будет выполняться:

>>> def errcheck(result, func, args):
...     if not result:
...         raise WinError()
...     rc = args[1]
...     return rc.left, rc.top, rc.bottom, rc.right
...
>>> GetWindowRect.errcheck = errcheck
>>>

Функции полезности

ctypes.addressof(obj)

Возвращает адрес буфера памяти в виде целого числа. obj должен быть экземпляром типа ctypes.

Создает auditing event ctypes.addressof с аргументом obj.

ctypes.alignment(obj_or_type)

Возвращает требования к выравниванию для типа ctypes. obj_or_type должен быть типом или экземпляром ctypes.

ctypes.byref(obj[, offset])

Возвращает упрощенный указатель на obj, который должен быть экземпляром типа ctypes. Значение offset по умолчанию равно нулю и должно быть целым числом, которое будет добавлено к значению внутреннего указателя.

byref(obj, offset) соответствует этому коду на языке Си:

(((char *)&obj) + offset)

Возвращаемый объект может использоваться только как параметр вызова внешней функции. Он ведет себя аналогично pointer(obj), но построение выполняется намного быстрее.

ctypes.cast(obj, type)

Эта функция аналогична оператору cast в C. Она возвращает новый экземпляр type, который указывает на тот же блок памяти, что и obj. type должен быть типом указателя, а obj должен быть объектом, который можно интерпретировать как указатель.

ctypes.create_string_buffer(init_or_size, size=None)

Эта функция создает изменяемый символьный буфер. Возвращаемый объект представляет собой массив ctypes c_char.

init_or_size должно быть целым числом, определяющим размер массива, или объектом bytes, который будет использоваться для инициализации элементов массива.

Если в качестве первого аргумента указан объект bytes, размер буфера увеличивается на один элемент по сравнению с его длиной, так что последний элемент в массиве является символом завершения NUL. В качестве второго аргумента может быть передано целое число, которое позволяет указать размер массива, если не следует использовать длину байтов.

Создает auditing event ctypes.create_string_buffer с аргументами init, size.

ctypes.create_unicode_buffer(init_or_size, size=None)

Эта функция создает изменяемый буфер символов unicode. Возвращаемый объект представляет собой массив ctypes, содержащий c_wchar.

init_or_size должно быть целым числом, определяющим размер массива, или строкой, которая будет использоваться для инициализации элементов массива.

Если в качестве первого аргумента указана строка, размер буфера увеличивается на один элемент по сравнению с длиной строки, так что последний элемент в массиве является символом завершения NUL. В качестве второго аргумента может быть передано целое число, которое позволяет указать размер массива, если не следует использовать длину строки.

Создает auditing event ctypes.create_unicode_buffer с аргументами init, size.

ctypes.DllCanUnloadNow()

Только для Windows: Эта функция представляет собой дополнение, позволяющее внедрять внутрипроцессные COM-серверы с ctypes. Она вызывается из функции DllCanUnloadNow, которую экспортирует библиотека расширения _ctypes.

ctypes.DllGetClassObject()

Только для Windows: Эта функция является подсчетом, позволяющим внедрять внутрипроцессные COM-серверы с ctypes. Она вызывается из функции DllGetClassObject, которую экспортирует библиотека расширения _ctypes.

ctypes.util.find_library(name)

Попробуйте найти библиотеку и вернуть путь к ней. name - это имя библиотеки без какого-либо префикса типа lib, суффикса типа .so, .dylib или номера версии (это форма, используемая для параметра компоновщика posix -l). Если библиотека не найдена, возвращает None.

Точная функциональность зависит от системы.

ctypes.util.find_msvcrt()

Только для Windows: возвращает имя файла библиотеки времени выполнения VC, используемой Python и модулями расширения. Если имя библиотеки не может быть определено, возвращается None.

Если вам нужно освободить память, например, выделенную модулем расширения с помощью вызова free(void *), важно, чтобы вы использовали функцию в той же библиотеке, которая выделила память.

ctypes.FormatError([code])

Только для Windows: Возвращает текстовое описание кода ошибки code. Если код ошибки не указан, используется последний код ошибки при вызове функции Windows api GetLastError.

ctypes.GetLastError()

Только для Windows: возвращает последний код ошибки, заданный Windows в вызывающем потоке. Эта функция вызывает функцию Windows GetLastError() напрямую, она не возвращает личную копию кода ошибки ctypes.

ctypes.get_errno()

Возвращает текущее значение ctypes-частной копии системной переменной errno в вызывающем потоке.

Выдает auditing event ctypes.get_errno без каких-либо аргументов.

ctypes.get_last_error()

Только для Windows: возвращает текущее значение ctypes-частной копии системной переменной LastError в вызывающем потоке.

Выдает auditing event ctypes.get_last_error без каких-либо аргументов.

ctypes.memmove(dst, src, count)

Аналогично стандартной библиотечной функции C memmove: копирует count байт из src в dst. dst и src должны быть целыми числами или экземплярами ctypes, которые могут быть преобразованы в указатели.

ctypes.memset(dst, c, count)

Аналогично стандартной библиотечной функции C memset: заполняет блок памяти по адресу dst значением count в байтах. dst должно быть целым числом, указывающим адрес, или экземпляром ctypes.

ctypes.POINTER(type, /)

Создайте и верните новый тип указателя ctypes. Типы указателей кэшируются и повторно используются внутри системы, поэтому повторный вызов этой функции обходится дешево. type должен быть типом ctypes.

ctypes.pointer(obj, /)

Создайте новый экземпляр указателя, указывающий на obj. Возвращаемый объект имеет тип POINTER(type(obj)).

Примечание: Если вы просто хотите передать указатель на объект вызову внешней функции, вам следует использовать byref(obj), что намного быстрее.

ctypes.resize(obj, size)

Эта функция изменяет размер буфера внутренней памяти obj, который должен быть экземпляром типа ctypes. Невозможно уменьшить размер буфера по сравнению с исходным размером типа objects, как указано в sizeof(type(obj)), но можно увеличить размер буфера.

ctypes.set_errno(value)

Установите текущее значение переменной ctypes-частной копии системной переменной errno в вызывающем потоке равным value и верните предыдущее значение.

Создает auditing event ctypes.set_errno с аргументом errno.

ctypes.set_last_error(value)

Только для Windows: установите текущее значение переменной ctypes-частной копии системной LastError в вызывающем потоке равным value и верните предыдущее значение.

Создает auditing event ctypes.set_last_error с аргументом error.

ctypes.sizeof(obj_or_type)

Возвращает размер в байтах буфера памяти типа ctypes или экземпляра. Выполняет те же действия, что и оператор C sizeof.

ctypes.string_at(address, size=-1)

Эта функция возвращает строку на языке Си, начинающуюся с адреса памяти address, в виде объекта bytes. Если указан размер, он используется в качестве size, в противном случае предполагается, что строка заканчивается нулем.

Создает auditing event ctypes.string_at с аргументами address, size.

ctypes.WinError(code=None, descr=None)

Только для Windows: эта функция, вероятно, имеет самое неудачное название в ctypes. Она создает экземпляр OSError. Если code не указан, для определения кода ошибки вызывается GetLastError. Если параметр descr не указан, вызывается FormatError() для получения текстового описания ошибки.

Изменено в версии 3.3: Раньше создавался экземпляр WindowsError, который теперь является псевдонимом OSError.

ctypes.wstring_at(address, size=-1)

Эта функция возвращает расширенную символьную строку, начинающуюся с адреса памяти address, в виде строки. Если указан размер *, он используется в качестве количества символов в строке, в противном случае предполагается, что строка заканчивается нулем.

Создает auditing event ctypes.wstring_at с аргументами address, size.

Типы данных

class ctypes._CData

Этот закрытый класс является общим базовым классом для всех типов данных types. Помимо прочего, все экземпляры типа ctypes содержат блок памяти, в котором хранятся данные, совместимые с C; адрес блока памяти возвращается вспомогательной функцией addressof(). Другая переменная экземпляра представлена как _objects; она содержит другие объекты Python, которые необходимо сохранить активными на случай, если блок памяти содержит указатели.

Общие методы типов данных ctypes, это все методы класса (если быть точным, это методы metaclass):

from_buffer(source[, offset])

Этот метод возвращает экземпляр ctypes, который совместно использует буфер исходного объекта. Исходный объект должен поддерживать интерфейс буфера, доступный для записи. Необязательный параметр offset определяет смещение в исходном буфере в байтах; значение по умолчанию равно нулю. Если исходный буфер недостаточно велик, генерируется значение ValueError.

Создает auditing event ctypes.cdata/buffer с аргументами pointer, size, offset.

from_buffer_copy(source[, offset])

Этот метод создает экземпляр ctypes, копируя буфер из буфера объекта source, который должен быть доступен для чтения. Необязательный параметр offset определяет смещение в исходном буфере в байтах; значение по умолчанию равно нулю. Если исходный буфер недостаточно велик, генерируется значение ValueError.

Создает auditing event ctypes.cdata/buffer с аргументами pointer, size, offset.

from_address(address)

Этот метод возвращает экземпляр типа ctypes, используя память, указанную в address, которая должна быть целым числом.

Создает auditing event ctypes.cdata с аргументом address.

from_param(obj)

Этот метод адаптирует obj к типу ctypes. Он вызывается с использованием фактического объекта, используемого при вызове внешней функции, когда тип присутствует в кортеже внешней функции argtypes; он должен возвращать объект, который может использоваться в качестве параметра вызова функции.

Все типы данных ctypes имеют реализацию этого метода класса по умолчанию, которая обычно возвращает obj, если это экземпляр типа. Некоторые типы также принимают другие объекты.

in_dll(library, name)

Этот метод возвращает экземпляр типа ctypes, экспортированный общей библиотекой. name - это название символа, который экспортирует данные, library - загруженная общая библиотека.

Общие переменные экземпляра типов типы данных:

_b_base_

Иногда экземпляры данных ctypes не владеют содержащимся в них блоком памяти, вместо этого они совместно используют часть блока памяти базового объекта. Элемент _b_base_, доступный только для чтения, является корневым объектом ctypes, которому принадлежит блок памяти.

_b_needsfree_

Эта переменная, доступная только для чтения, имеет значение true, когда экземпляр данных ctypes сам выделил блок памяти, в противном случае значение false.

_objects

Этот элемент является либо None, либо словарем, содержащим объекты Python, которые необходимо поддерживать в рабочем состоянии, чтобы содержимое блока памяти оставалось действительным. Этот объект доступен только для отладки; никогда не изменяйте содержимое этого словаря.

Основные типы данных

class ctypes._SimpleCData

Этот закрытый класс является базовым классом для всех типов данных fundamental types. Он упоминается здесь потому, что содержит общие атрибуты типов данных fundamental ctypes. _SimpleCData является подклассом _CData, поэтому он наследует их методы и атрибуты. теперь можно выбирать типы данных ctypes, которые не являются указателями и не содержат их.

Экземпляры имеют один атрибут:

value

Этот атрибут содержит фактическое значение экземпляра. Для типов integer и pointer это целое число, для символьных типов - объект или строка в байтах с одним символом, для типов символьных указателей - объект или строка в байтах Python.

Когда атрибут value извлекается из экземпляра ctypes, обычно каждый раз возвращается новый объект. ctypes не реализует возврат исходного объекта, всегда создается новый объект. То же самое верно для всех экземпляров объектов других типов.

Основные типы данных, возвращаемые в виде результатов вызова внешней функции или, например, путем извлечения элементов структурного поля или массива, прозрачно преобразуются в собственные типы Python. Другими словами, если внешняя функция имеет restype из c_char_p, вы всегда будете получать объект Python bytes, а не экземпляр c_char_p.

Подклассы основных типов данных не наследуют это поведение. Таким образом, если внешняя функция restype является подклассом c_void_p, вы получите экземпляр этого подкласса при вызове функции. Конечно, вы можете получить значение указателя, обратившись к атрибуту value.

Это основные типы типы данных:

class ctypes.c_byte

Представляет тип данных C signed char и интерпретирует значение как малое целое число. Конструктор принимает необязательный целочисленный инициализатор; проверка переполнения не выполняется.

class ctypes.c_char

Представляет тип данных C char и интерпретирует значение как один символ. Конструктор принимает необязательный инициализатор строки, длина строки должна составлять ровно один символ.

class ctypes.c_char_p

Представляет тип данных C char*, когда он указывает на строку, заканчивающуюся нулем. Для общего символьного указателя, который также может указывать на двоичные данные, необходимо использовать POINTER(c_char). Конструктор принимает целочисленный адрес или объект в байтах.

class ctypes.c_double

Представляет тип данных C double. Конструктор принимает необязательный инициализатор с плавающей точкой.

class ctypes.c_longdouble

Представляет тип данных C long double. Конструктор принимает необязательный инициализатор с плавающей точкой. На платформах, где sizeof(long double) == sizeof(double) является псевдонимом для c_double.

class ctypes.c_float

Представляет тип данных C float. Конструктор принимает необязательный инициализатор с плавающей точкой.

class ctypes.c_int

Представляет тип данных C signed int. Конструктор принимает необязательный целочисленный инициализатор; проверка переполнения не выполняется. На платформах, где sizeof(int) == sizeof(long) является псевдонимом c_long.

class ctypes.c_int8

Представляет 8-разрядный код C :тип данных c:expr:signed int. Обычно это псевдоним для c_byte.

class ctypes.c_int16

Представляет 16-разрядный код C :тип данных C:expr:signed int. Обычно это псевдоним для c_short.

class ctypes.c_int32

Представляет 32-разрядный код C :тип данных c:expr:signed int. Обычно это псевдоним для c_int.

class ctypes.c_int64

Представляет 64-разрядный код C :тип данных c:expr:signed int. Обычно это псевдоним для c_longlong.

class ctypes.c_long

Представляет тип данных C signed long. Конструктор принимает необязательный целочисленный инициализатор; проверка переполнения не выполняется.

class ctypes.c_longlong

Представляет тип данных C signed long long. Конструктор принимает необязательный целочисленный инициализатор; проверка переполнения не выполняется.

class ctypes.c_short

Представляет тип данных C signed short. Конструктор принимает необязательный целочисленный инициализатор; проверка переполнения не выполняется.

class ctypes.c_size_t

Представляет тип данных C size_t.

class ctypes.c_ssize_t

Представляет тип данных C ssize_t.

Добавлено в версии 3.2.

class ctypes.c_ubyte

Представляет тип данных C unsigned char, он интерпретирует значение как малое целое число. Конструктор принимает необязательный целочисленный инициализатор; проверка переполнения не выполняется.

class ctypes.c_uint

Представляет тип данных C unsigned int. Конструктор принимает необязательный целочисленный инициализатор; проверка переполнения не выполняется. На платформах, где sizeof(int) == sizeof(long) является псевдонимом для c_ulong.

class ctypes.c_uint8

Представляет 8-разрядный код C :тип данных c:expr:unsigned int. Обычно это псевдоним для c_ubyte.

class ctypes.c_uint16

Представляет 16-разрядный код C :тип данных C:expr:unsigned int. Обычно это псевдоним для c_ushort.

class ctypes.c_uint32

Представляет 32-разрядный код C :тип данных c:expr:unsigned int. Обычно это псевдоним для c_uint.

class ctypes.c_uint64

Представляет 64-разрядный код C :тип данных c:expr:unsigned int. Обычно это псевдоним для c_ulonglong.

class ctypes.c_ulong

Представляет тип данных C unsigned long. Конструктор принимает необязательный целочисленный инициализатор; проверка переполнения не выполняется.

class ctypes.c_ulonglong

Представляет тип данных C unsigned long long. Конструктор принимает необязательный целочисленный инициализатор; проверка переполнения не выполняется.

class ctypes.c_ushort

Представляет тип данных C unsigned short. Конструктор принимает необязательный целочисленный инициализатор; проверка переполнения не выполняется.

class ctypes.c_void_p

Представляет тип C void*. Значение представлено в виде целого числа. Конструктор принимает необязательный целочисленный инициализатор.

class ctypes.c_wchar

Представляет тип данных C wchar_t и интерпретирует значение как односимвольную строку в юникоде. Конструктор принимает необязательный инициализатор строки, длина строки должна составлять ровно один символ.

class ctypes.c_wchar_p

Представляет тип данных C wchar_t*, который должен быть указателем на строку символов, заканчивающуюся нулем. Конструктор принимает целочисленный адрес или строку.

class ctypes.c_bool

Представляет тип данных C bool (точнее, _Bool из C99). Его значение может быть True или False, и конструктор принимает любой объект, имеющий значение истинности.

class ctypes.HRESULT

Только для Windows: Представляет значение HRESULT, которое содержит информацию об успешном завершении или ошибке вызова функции или метода.

class ctypes.py_object

Представляет тип данных C PyObject*. При вызове этого параметра без аргумента создается указатель NULL PyObject*.

Модуль ctypes.wintypes предоставляет некоторые другие типы данных, специфичные для Windows, например : c:type:HWND, WPARAM или DWORD. Также определены некоторые полезные структуры, такие как MSG или RECT.

Структурированные типы данных

class ctypes.Union(*args, **kw)

Абстрактный базовый класс для объединений в собственном порядке байтов.

class ctypes.BigEndianUnion(*args, **kw)

Абстрактный базовый класс для объединений в порядке байтов по *большому порядку следования байтов.

Добавлено в версии 3.11.

class ctypes.LittleEndianUnion(*args, **kw)

Абстрактный базовый класс для объединений в порядке байтов по строкам со строчной запятой.

Добавлено в версии 3.11.

class ctypes.BigEndianStructure(*args, **kw)

Абстрактный базовый класс для структур в *порядке следования по порядку байтов.

class ctypes.LittleEndianStructure(*args, **kw)

Абстрактный базовый класс для структур, расположенных в порядке байтов.

Структуры и объединения с нестандартным порядком байтов не могут содержать поля типа указателя или любые другие типы данных, содержащие поля типа указателя.

class ctypes.Structure(*args, **kw)

Абстрактный базовый класс для структур в собственном порядке байтов.

Конкретные типы структур и объединений должны быть созданы путем создания подкласса одного из этих типов и, по крайней мере, определения переменной класса _fields_. ctypes создаст descriptor, которые позволяют считывать и записывать поля с помощью прямого доступа к атрибутам. Это те самые

_fields_

Последовательность, определяющая структуру полей. Элементы должны состоять из 2-х или 3-х кортежей. Первый элемент - это название поля, второй элемент определяет тип поля; это может быть любой тип данных ctypes.

Для полей целочисленного типа, таких как c_int, может быть задан третий необязательный элемент. Это должно быть небольшое положительное целое число, определяющее разрядность поля.

Имена полей должны быть уникальными в пределах одной структуры или объединения. Этот флажок не установлен, при повторении имен можно получить доступ только к одному полю.

Можно определить переменную _fields_ class *после инструкции class, которая определяет подкласс Structure, это позволяет создавать типы данных, которые прямо или косвенно ссылаются сами на себя:

class List(Structure):
    pass
List._fields_ = [("pnext", POINTER(List)),
                 ...
                ]

Однако переменная класса _fields_ должна быть определена до первого использования типа (создается экземпляр, для него вызывается sizeof() и так далее). Более поздние присвоения переменной класса _fields_ вызовут ошибку атрибута.

Можно определить подклассы структурных типов, они наследуют поля базового класса плюс _fields_, определенные в подклассе, если таковые имеются.

_pack_

Необязательное маленькое целое число, которое позволяет переопределить выравнивание полей структуры в экземпляре. _pack_ должно быть уже определено при назначении _fields_, иначе это не будет иметь никакого эффекта.

_anonymous_

Необязательная последовательность, в которой перечислены имена неназванных (анонимных) полей. _anonymous_ должно быть уже определено при назначении _fields_, в противном случае это не будет иметь никакого эффекта.

Поля, перечисленные в этой переменной, должны быть полями типа structure или union. ctypes в типе structure будут созданы дескрипторы, позволяющие напрямую обращаться к вложенным полям без необходимости создания поля structure или union.

Вот пример типа (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)

Можно определить подклассы структур, они наследуют поля базового класса. Если в определении подкласса есть отдельная переменная _fields_, поля, указанные в ней, добавляются к полям базового класса.

Конструкторы Structure и union принимают как позиционные, так и ключевые аргументы. Позиционные аргументы используются для инициализации полей-членов в том же порядке, в котором они отображаются в _fields_. Аргументы ключевого слова в конструкторе интерпретируются как присвоения атрибутов, поэтому они будут инициализировать _fields_ с тем же именем или создавать новые атрибуты для имен, отсутствующих в _fields_.

Массивы и указатели

class ctypes.Array(*args)

Абстрактный базовый класс для массивов.

Рекомендуемый способ создания конкретных типов массивов - умножение любого типа данных ctypes на неотрицательное целое число. В качестве альтернативы вы можете создать подкласс этого типа и определить переменные класса _length_ и _type_. Элементы массива могут быть считаны и записаны с использованием стандартных индексов и срезов; при чтении срезов результирующий объект сам по себе не является Array.

_length_

Положительное целое число, указывающее количество элементов в массиве. Индексы, находящиеся вне диапазона, приводят к IndexError. Будет возвращено значение len().

_type_

Определяет тип каждого элемента в массиве.

Конструкторы подкласса Array принимают позиционные аргументы, используемые для инициализации элементов по порядку.

class ctypes._Pointer

Частный, абстрактный базовый класс для указателей.

Конкретные типы указателей создаются путем вызова POINTER() с типом, на который будет указано; это делается автоматически с помощью pointer().

Если указатель указывает на массив, его элементы могут быть прочитаны и записаны с использованием стандартных индексов и срезов. Объекты-указатели не имеют размера, поэтому len() приведет к TypeError. Отрицательные индексы будут считываться из памяти перед указателем (как в C), а индексы, находящиеся вне диапазона, вероятно, приведут к сбою из-за нарушения доступа (если вам повезет).

_type_

Определяет указанный тип.

contents

Возвращает объект, на который указывает to pointer. Присвоение этому атрибуту изменяет указатель так, чтобы он указывал на назначенный объект.

Вернуться на верх