Spec-Zone.ru › Python 3.9

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

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

Учебник по ctypes

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

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

Загрузка динамических библиотек

ctypes экспортирует объекты cdll, а в Windows — windll и oledll для загрузки динамических библиотек.

Библиотеки загружаются путем доступа к ним как к атрибутам этих объектов. cdll загружает библиотеки, которые экспортируют функции, используя стандартную cdecl конвенцию вызова, в то время как windll библиотеки вызывают функции, используя stdcall конвенцию вызова. oledll также использует stdcall конвенцию вызова и предполагает, что функции возвращают код ошибки Windows HRESULT. Код ошибки используется для автоматического поднятия исключения OSError при неудачном вызове функции.

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

Вот несколько примеров для Windows. Обратите внимание, что msvcrt — это стандартная библиотека MS C, содержащая большинство стандартных функций C и использующая конвенцию вызова cdecl:

>>> from ctypes import *
>>> print(windll.kernel32)  
<WinDLL 'kernel32', handle ... at ...>
>>> print(cdll.msvcrt)      
<CDLL 'msvcrt', handle ... at ...>
>>> libc = cdll.msvcrt      
>>>

Windows автоматически добавляет обычное .dll расширение файла.

Примечание

Доступ к стандартной библиотеке C через cdll.msvcrt будет использовать устаревшую версию библиотеки, которая может быть несовместима с используемой Python. Если это возможно, используйте собственные возможности Python, в противном случае импортируйте и используйте модуль msvcrt.

В Linux необходимо указывать имя файла включая расширение для загрузки библиотеки, поэтому доступ к атрибутам для загрузки библиотек невозможен. Должно быть использовано LoadLibrary() метод загрузчиков dll, или вы должны загрузить библиотеку, создав экземпляр CDLL, вызвав конструктор:

>>> cdll.LoadLibrary("libc.so.6")  
<CDLL 'libc.so.6', handle ... at ...>
>>> libc = CDLL("libc.so.6")       
>>> libc                           
<CDLL 'libc.so.6', handle ... at ...>
>>>

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

Функции доступны как атрибуты объектов dll:

>>> from ctypes import *
>>> 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, которая возвращает дескриптор модуля для заданного имени модуля, имеет следующий прототип C, и используется макрос, чтобы экспонировать одну из них как GetModuleHandle в зависимости от того, определён UNICODE или нет:

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

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

Иногда 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, байтовые объекты и строки передаются как указатели на блок памяти, содержащий их данные (char * или wchar_t *). Целые числа Python передаются как стандартный тип C платформы int, их значение маскируется для соответствия типу C.

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

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

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

Тип ctypes

Тип C

Тип Python

c_bool

_Bool

bool (1)

c_char

char

объект bytes длиной в 1 символ

c_wchar

wchar_t

строка длиной в 1 символ

c_byte

char

int

c_ubyte

unsigned char

int

c_short

short

int

c_ushort

unsigned short

int

c_int

int

int

c_uint

unsigned int

int

c_long

long

int

c_ulong

unsigned long

int

c_longlong

__int64 или long long

int

c_ulonglong

unsigned __int64 или unsigned long long

int

c_size_t

size_t

int

c_ssize_t

ssize_t или Py_ssize_t

int

c_float

float

float

c_double

double

float

c_longdouble

long double

float

c_char_p

char * (с завершением NUL)

объект bytes или None

c_wchar_p

wchar_t * (с завершением NUL)

строка или 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; если вы хотите получить доступ к нему как к строке с завершением NUL, используйте свойство 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_string() из более ранних выпусков ctypes. Чтобы создать изменяемый блок памяти, содержащий символы Юникода типа 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: exceptions.TypeError: Don't know how to convert parameter 2
>>>

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

Вот более продвинутый пример, в котором используется функция 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: exceptions.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 — это функция, которая вызовет API Windows FormatMessage() для получения строкового представления кода ошибки и возвратит исключение. 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: структура, объединение, массив, указатель.

Вот простой пример структуры 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>
>>>

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

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

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

По умолчанию поля структуры и объединения выравниваются так же, как это делает компилятор C. Это поведение можно переопределить, указав атрибут класса _pack_ в определении подкласса. Он должен быть установленным положительным целым числом и определяет максимальное выравнивание для полей. Именно это также делает #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 POINT среди прочего:

>>> 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 не имеет OOR (возврат исходного объекта), он создает каждый раз новый эквивалентный объект, когда вы получаете атрибут:

>>> 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...>
>>>

Вызов типа указателя без аргумента создаёт 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), ctypes принимает массив 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() в этом случае.

Чтобы установить поле типа POINTER в 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 недоступен в самом операторе класса. В ctypes мы можем определить класс cell и установить атрибут _fields_ позже, после оператора класса:

>>> 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.

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

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

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

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

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

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

Так как table является указателем на массив записей 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
__hello__ 161
__phello__ -161
__phello__.spam 161
>>>

Тот факт, что стандартный 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 — это объекты, содержащие блок памяти плюс некоторые дескрипторы, которые обращаются к содержимому памяти. Хранение объекта Python в блоке памяти не хранит сам объект, вместо этого хранится contents объекта. При повторном доступе к содержимому каждый раз создаётся новый объект Python!

Переменные типы данных

ctypes предоставляет некоторую поддержку для массивов и структур с переменным размером.

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

Это хорошо и замечательно, но как получить доступ к дополнительным элементам, содержащимся в этом массиве? Поскольку тип всё ещё знает только о 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, так как API Windows не возвращает эту информацию, что затрудняет диагностику ошибки. Чтобы устранить эту ошибку и определить, какая DLL не найдена, вам необходимо найти список зависимых DLL и определить, какая из них отсутствует, используя инструменты отладки и трассировки Windows.

См. также

Инструмент Microsoft DUMPBIN – инструмент для поиска зависимых 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 использовался ранее.

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

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

Блокировка глобального интерпретатора Python (GIL) освобождается перед вызовом любой функции, экспортированной этими библиотеками, и приобретается снова после этого.

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

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

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

Все эти классы могут быть созданы, вызвав их с по меньшей мере одним аргументом, путём к общей библиотеке. Если у вас есть существующая обработка уже загруженной общей библиотеки, её можно передать в качестве параметра 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, управляемый функциями API Windows GetLastError() и SetLastError(); для запроса и изменения копии ctypes кода ошибки Windows используются ctypes.get_last_error() и ctypes.set_last_error().

Параметр winmode используется в Windows для указания способа загрузки библиотеки (так как mode игнорируется). Он принимает любое значение, допустимое для параметра флагов API Win32 LoadLibraryEx. При отсутствии значения используется способ, обеспечивающий наиболее безопасную загрузку 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 C Python доступен готовый объект общей библиотеки Python:

ctypes.pythonapi

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

Загрузка библиотеки через любой из этих объектов вызывает событие аудита аудита ctypes.dlopen со строковым аргументом name, именем, используемым для загрузки библиотеки.

Обращение к функции загруженной библиотеки вызывает событие аудита ctypes.dlsym с аргументами library (объект библиотеки) и name (имя символа в виде строки или целого числа).

В тех случаях, когда доступен только дескриптор библиотеки, а не сам объект, обращение к функции вызывает событие аудита ctypes.dlsym/handle с аргументами handle (сырой дескриптор библиотеки) и name.

Внешние функции

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

class ctypes._FuncPtr

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

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

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

restype

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

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

argtypes

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

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

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

errcheck

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

callable(result, func, arguments)

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

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

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

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

exception ctypes.ArgumentError

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

В Windows, когда вызов внешней функции вызывает системное исключение (например, из-за нарушения доступа), оно будет перехвачено и заменено соответствующим исключением Python. Кроме того, будет вызвано событие аудита ctypes.seh_exception с аргументом code , позволяя крючку аудита заменить исключение своим собственным.

Некоторые способы вызова внешних функций могут вызвать событие аудита ctypes.call_function с аргументами function pointer и arguments.

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

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

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

Возвращаемый прототип функции создаёт функции, использующие стандартное соглашение о вызове C. Функция освободит GIL во время вызова. Если use_errno установлено в true, внутренняя копия ctypes переменной системы 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 callable.

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, которую должен предоставить вызывающий код. Вот C-объявление:

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 возвращает 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.

Вызывает событие аудита событие аудита ctypes.addressof с аргументом obj.

ctypes.alignment(obj_or_type)

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

ctypes.byref(obj[, offset])

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

byref(obj, offset) соответствует этому коду C:

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

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

ctypes.cast(obj, type)

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

ctypes.create_string_buffer(init_or_size, size=None)

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

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

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

Вызывает событие аудита событие аудита ctypes.create_string_buffer с аргументами init, size.

ctypes.create_unicode_buffer(init_or_size, size=None)

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

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

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

Вызывает событие аудита событие аудита ctypes.create_unicode_buffer с аргументами init, size.

ctypes.DllCanUnloadNow()

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

ctypes.DllGetClassObject()

Только Windows: Эта функция — обработчик, позволяющий реализовывать COM-серверы в процессе с помощью ctypes. Она вызывается из функции DllGetClassObject, которую экспортирует dll расширения _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()

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

Вызывает событие аудита ctypes.get_errno без аргументов.

ctypes.get_last_error()

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

Вызывает событие аудита 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 байтами значения c. dst должен быть целым числом, определяющим адрес, или экземпляром ctypes.

ctypes.POINTER(type)

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

ctypes.pointer(obj)

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

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

ctypes.resize(obj, size)

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

ctypes.set_errno(value)

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

Вызывает событие аудита ctypes.set_errno с аргументом errno.

ctypes.set_last_error(value)

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

Вызывает событие аудита ctypes.set_last_error с аргументом error.

ctypes.sizeof(obj_or_type)

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

ctypes.string_at(address, size=-1)

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

Вызывает событие аудита ctypes.string_at с аргументами address, size.

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

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

Изменено в версии 3.3: Создавался экземпляр WindowsError.

ctypes.wstring_at(address, size=-1)

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

Вызывает событие аудита ctypes.wstring_at с аргументами address, size.

Типы данных

class ctypes._CData

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

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

from_buffer(source[, offset])

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

Возбуждает событие аудита аудита ctypes.cdata/buffer с аргументами pointer, size, offset.

from_buffer_copy(source[, offset])

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

Возбуждает событие аудита аудита ctypes.cdata/buffer с аргументами pointer, size, offset.

from_address(address)

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

Этот метод и другие методы, косвенно вызывающие этот метод, возбуждают событие аудита аудита ctypes.cdata с аргументом address.

from_param(obj)

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

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

in_dll(library, name)

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

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

_b_base_

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

_b_needsfree_

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

_objects

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

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

class ctypes._SimpleCData

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

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

value

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

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

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

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

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

class ctypes.c_byte

Представляет тип данных C signed char, и интерпретирует значение как целое число.

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

class ctypes.c_char

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

class ctypes.c_char_p

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

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

Представляет тип данных C 8-битного signed int типа. Обычно является псевдонимом для c_byte.

class ctypes.c_int16

Представляет тип данных C 16-битного signed int типа. Обычно является псевдонимом для c_short.

class ctypes.c_int32

Представляет тип данных C 32-битного signed int типа. Обычно является псевдонимом для c_int.

class ctypes.c_int64

Представляет тип данных C 64-битного 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

Представляет тип данных C 8-битного unsigned int типа. Обычно является псевдонимом для c_ubyte.

class ctypes.c_uint16

Представляет тип данных C 16-битного unsigned int типа. Обычно является псевдонимом для c_ushort.

class ctypes.c_uint32

Представляет тип данных C 32-битного unsigned int типа. Обычно является псевдонимом для c_uint.

class ctypes.c_uint64

Представляет тип данных C 64-битного 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, и интерпретирует значение как одиночную строку Unicode. Конструктор принимает необязательный строковый инициализатор, длина строки должна быть ровно один символ.

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, например, HWND, WPARAM или DWORD. Также определены некоторые полезные структуры, например, MSG или RECT.

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

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

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

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

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

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

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

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

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

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

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

_fields_

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

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

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

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

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

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

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

_pack_

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

_anonymous_

Необязательная последовательность, перечисляющая имена безымянных (анонимных) полей. _anonymous_ должно быть определено до присвоения _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)

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

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

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

class ctypes.Array(*args)

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

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

_length_

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

_type_

Указывает тип каждого элемента в массиве.

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

class ctypes._Pointer

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

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

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

_type_

Указывает тип, на который указывает указатель.

contents

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

© 2001–2022 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.9/library/ctypes.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API