Spec-Zone.ru › Python 3.12

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

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

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 — это стандартная библиотека C от MS, содержащая большинство стандартных функций 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:

>>> 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, а затем вызвать её, передав соответственно объект bytes или строку.

Иногда 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. В этом примере используется функция rand(), которая не принимает аргументов и возвращает псевдослучайное целое число:

>>> print(libc.rand())
1804289383

В Windows можно вызвать функцию GetModuleHandleA(), которая возвращает дескриптор модуля win32 (передав None в качестве единственного аргумента, чтобы вызвать её с указателем NULL):

>>> 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, целые числа, объекты bytes и строки (unicode) — единственные встроенные объекты Python, которые можно непосредственно использовать в качестве параметров этих вызовов функций. None передаётся как указатель C NULL, объекты bytes и строки передаются как указатель на блок памяти, содержащий их данные (char* или wchar_t*). Целые числа Python передаются как тип C int, используемый платформой по умолчанию; их значение маскируется, чтобы поместиться в тип C.

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

Фундаментальные типы данных

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

Тип ctypes

Тип C

Тип Python

c_bool

_Bool

bool (1)

c_char

char

объект bytes из одного байта

c_wchar

wchar_t

строка из одного символа

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_time_t

time_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 изменяет адрес памяти, на который они указывают, а не содержимое блока памяти (конечно, это ожидаемо, поскольку объекты bytes в Python неизменяемы):

>>> 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() (которая по-прежнему доступна как псевдоним). Чтобы создать изменяемый блок памяти, содержащий символы unicode типа 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, кроме целых чисел, строк и объектов 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_ и использует его как аргумент функции. Атрибут должен содержать целое число, строку, объект bytes, экземпляр 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. И здесь результатом должно быть целое число, строка, объект bytes, экземпляр ctypes или объект с атрибутом _as_parameter_.

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

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

Прототип C функции time() — time_t time(time_t *). Поскольку time_t может иметь тип, отличный от типа возвращаемого значения по умолчанию int, необходимо задать атрибут restype:

>>> libc.time.restype = c_time_t

Типы аргументов можно указать с помощью argtypes:

>>> libc.time.argtypes = (POINTER(c_time_t),)

Чтобы вызвать функцию, передав указатель NULL в качестве первого аргумента, используйте None:

>>> print(libc.time(None))
1150640792

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

>>> 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, и тогда второй аргумент будет преобразован из односимвольного объекта bytes Python в тип C char:

>>> strchr.restype = c_char_p
>>> strchr.argtypes = [c_char_p, c_char]
>>> strchr(b"abcdef", b"d")
b'def'
>>> strchr(b"abcdef", b"def")
Traceback (most recent call last):
ctypes.ArgumentError: argument 2: TypeError: one character bytes, bytearray or integer expected
>>> print(strchr(b"abcdef", b"x"))
None
>>> strchr(b"abcdef", b"d")
b'def'
>>>

В качестве атрибута restype также можно использовать вызываемый объект Python (например, функцию или класс), если внешняя функция возвращает целое число. Вызываемый объект будет вызван с целым числом, возвращённым функцией 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 API FormatMessage() для получения строкового представления кода ошибки и возвращает исключение. WinError принимает необязательный параметр — код ошибки; если он не указан, функция вызывает GetLastError(), чтобы получить его.

Обратите внимание: более мощный механизм проверки ошибок доступен через атрибут errcheck; подробности см. в справочном руководстве.

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

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

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

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

Тип поля должен быть типом 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, библиотека не гарантирует работоспособность в общем случае. Объединения и структуры с битовыми полями следует всегда передавать в функции по указателю.

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

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

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 строго проверяет типы. Это означает, что если в списке argtypes функции или в качестве типа поля-члена в определении структуры указано POINTER(c_int), принимаются только экземпляры точно такого же типа. Из этого правила есть некоторые исключения: 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 позволяет создавать из вызываемых объектов Python указатели на функции, которые можно вызывать из C. Такие функции иногда называют функциями обратного вызова.

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

Фабричная функция 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_Version — номер версии среды выполнения Python, закодированный одним целочисленным значением.

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

>>> version = ctypes.c_int.in_dll(ctypes.pythonapi, "Py_Version")
>>> print(hex(version.value))
0x30c00a0

Расширенный пример, также демонстрирующий использование указателей, обращается к указателю 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 не даёт ожидаемого результата.

Помните, что при получении вложенных объектов из Structure, Unions и Arrays вложенный объект не копируется: вместо этого возвращается объект-обёртка, обращающийся к базовому буферу корневого объекта.

Вот ещё один пример, поведение которого может отличаться от ожидаемого:

>>> 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] The specified module could not be found». В этом сообщении об ошибке не указано имя отсутствующей DLL, поскольку API Windows не предоставляет эту информацию, что затрудняет диагностику ошибки. Чтобы устранить эту ошибку и определить, какая DLL не найдена, нужно получить список зависимых DLL и с помощью средств отладки и трассировки Windows определить, какая из них отсутствует.

Изменено в версии 3.12: Параметр name теперь может быть объектом, подобным пути.

См. также

Утилита Microsoft DUMPBIN — средство для поиска зависимостей DLL.

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

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

Доступность: Windows

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

Изменено в версии 3.12: Параметр name теперь может быть объектом, подобным пути.

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

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

Доступность: Windows

Изменено в версии 3.12: Параметр name теперь может быть объектом, подобным пути.

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

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

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

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

Изменено в версии 3.12: Параметр name теперь может быть объектом, подобным пути.

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

Параметр winmode используется в Windows для указания способа загрузки библиотеки (поскольку параметр mode игнорируется). Ему можно присвоить любое значение, допустимое для параметра flags функции 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

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

Доступность: Windows

ctypes.oledll

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

Доступность: Windows

ctypes.pydll

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

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

ctypes.pythonapi

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

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

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

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

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

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

Это экземпляры закрытого локального класса _FuncPtr (не предоставляемого в ctypes), который наследуется от закрытого класса _CFuncPtr:

>>> import ctypes
>>> lib = ctypes.CDLL(None)
>>> issubclass(lib._FuncPtr, ctypes._CFuncPtr)
True
>>> lib._FuncPtr is ctypes._CFuncPtr
False
class ctypes._CFuncPtr

Базовый класс вызываемых из 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 преобразует переданную в качестве аргумента строку в объект bytes согласно правилам преобразования ctypes.

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

errcheck

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

callable(result, func, arguments)

result — значение, возвращённое внешней функцией, согласно атрибуту restype.

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

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

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

exception ctypes.ArgumentError

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

В Windows системное исключение, возникшее при вызове внешней функции (например, из-за нарушения доступа), перехватывается и заменяется подходящим исключением Python. Кроме того, возникает событие аудита ctypes.set_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, приватная копия системной переменной errno в ctypes обменивается значением с настоящим значением errno до и после вызова; use_last_error делает то же самое для кода ошибки Windows.

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

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

Доступность: Windows

ctypes.PYFUNCTYPE(restype, *argtypes)

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

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

prototype(address)

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

prototype(callable)

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

prototype(func_spec[, paramflags])

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

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

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

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

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

ctypes.DllCanUnloadNow()

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

Доступность: Windows

ctypes.DllGetClassObject()

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

Доступность: Windows

ctypes.util.find_library(name)

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

Точное поведение зависит от системы.

ctypes.util.find_msvcrt()

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

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

Доступность: Windows

ctypes.FormatError([code])

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

Доступность: Windows

ctypes.GetLastError()

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

Доступность: Windows

ctypes.get_errno()

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

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

ctypes.get_last_error()

Возвращает текущее значение приватной копии системной переменной LastError в ctypes для вызывающего потока.

Доступность: Windows

Вызывает событие аудита 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 значением c в течение count байт. 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 в ctypes для вызывающего потока в значение value и возвращает предыдущее значение.

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

ctypes.set_last_error(value)

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

Доступность: Windows

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

ctypes.sizeof(obj_or_type)

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

ctypes.string_at(ptr, size=-1)

Возвращает строку байтов по адресу void *ptr. Если задан параметр size, он используется как размер; иначе считается, что строка завершается нулевым байтом.

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

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

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

Доступность: Windows

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

ctypes.wstring_at(ptr, size=-1)

Возвращает строку широких символов по адресу void *ptr. Если задан параметр size, он используется как количество символов строки; иначе считается, что строка завершается нулевым символом.

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

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

value

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

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

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

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

class ctypes.c_int16

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

class ctypes.c_int32

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

class ctypes.c_int64

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

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

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

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 unsigned int. Обычно является псевдонимом c_ubyte.

class ctypes.c_uint16

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

class ctypes.c_uint32

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

class ctypes.c_uint64

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

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

Доступность: Windows

class ctypes.py_object

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

Модуль ctypes.wintypes предоставляет и другие типы данных, специфичные для Windows, например 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 создаёт дескрипторы, позволяющие читать и записывать поля напрямую через обращения к атрибутам. Это

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

_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–2024 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.12/library/ctypes.html

Spec-Zone.ru

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