Spec-Zone.ru › Python 3.14

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

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

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

Это необязательный модуль. Если он отсутствует в вашей копии CPython, обратитесь к документации вашего дистрибутива (то есть к документации того, кто предоставил вам Python). Если вы являетесь поставщиком дистрибутива, см. раздел Требования к необязательным модулям.

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

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

Руководство по ctypes

Примечание. В некоторых примерах кода используется тип 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 от Microsoft, содержащая большинство стандартных функций 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.

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

Например, в Linux:

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

В macOS:

>>> cdll.LoadLibrary("libc.dylib")
<CDLL 'libc.dylib', handle ... at ...>
>>> libc = CDLL("libc.dylib")
>>> libc
<CDLL 'libc.dylib', 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
>>>

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

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

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

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

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

Тип ctypes

Тип C

Тип Python

_type_

c_bool

_Bool

bool

'?'

c_char

char

bytes длиной в 1 символ

'c'

c_wchar

wchar_t

str длиной в 1 символ

'u'

c_byte

char

int

'b'

c_ubyte

unsigned char

int

'B'

c_short

short

int

'h'

c_ushort

unsigned short

int

'H'

c_int

int

int

'i' *

c_int8

int8_t

int

*

c_int16

int16_t

int

*

c_int32

int32_t

int

*

c_int64

int64_t

int

*

c_uint

unsigned int

int

'I' *

c_uint8

uint8_t

int

*

c_uint16

uint16_t

int

*

c_uint32

uint32_t

int

*

c_uint64

uint64_t

int

*

c_long

long

int

'l'

c_ulong

unsigned long

int

'L'

c_longlong

long long

int

'q' *

c_ulonglong

unsigned long long

int

'Q' *

c_size_t

size_t

int

*

c_ssize_t

Py_ssize_t

int

*

c_time_t

time_t

int

*

c_float

float

float

'f'

c_double

double

float

'd'

c_longdouble

long double

float

'g' *

c_char_p

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

bytes или None

'z'

c_wchar_p

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

str или None

'Z'

c_void_p

void*

int или None

'P'

py_object

PyObject*

object

'O'

VARIANT_BOOL

short int

bool

'v'

Кроме того, если арифметика комплексных чисел, совместимая с IEC 60559 (приложение G), поддерживается и в C, и в libffi, доступны следующие комплексные типы:

Тип ctypes

Тип C

Тип Python

_type_

c_float_complex

float complex

complex

'F'

c_double_complex

double complex

complex

'D'

c_longdouble_complex

long double complex

complex

'G'

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

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

Конструкторы числовых типов преобразуют входные данные с помощью __bool__(), __index__() (для int), __float__() или __complex__(). Это означает, что c_bool принимает любой объект, имеющий логическое значение:

>>> empty_list = []
>>> c_bool(empty_list)
c_bool(False)

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

>>> 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 неизменяемы):

>>> 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>
ctypes.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>
ctypes.ArgumentError: argument 2: TypeError: 'int' object cannot be interpreted as ctypes.c_char_p
>>> 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, и тогда второй аргумент будет преобразован из однобайтового объекта Python bytes в символ типа 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 — функция, которая вызывает API Windows FormatMessage() для получения строкового представления кода ошибки и возвращает исключение. WinError принимает необязательный параметр — код ошибки; если он не задан, функция вызывает GetLastError(), чтобы получить его.

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

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

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

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

Тип поля должен быть типом 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))

Дескрипторы полей можно получить из класса; они полезны при отладке, поскольку могут предоставлять полезную информацию. См. CField:

>>> POINT.x
<ctypes.CField 'x' type=c_int, ofs=0, size=4>
>>> POINT.y
<ctypes.CField 'y' type=c_int, ofs=4, size=4>
>>>

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

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

Расположение, выравнивание и порядок байтов структур и объединений

По умолчанию поля структур и объединений располагаются так же, как это делает компилятор C. Это поведение можно полностью переопределить, задав атрибут класса _layout_ в определении подкласса; подробности см. в документации атрибута.

Максимальное выравнивание полей и/или самой структуры можно задать с помощью атрибутов класса _pack_ и/или _align_ соответственно. Подробности см. в документации атрибутов.

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

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

Можно создавать структуры и объединения, содержащие битовые поля. Битовые поля допустимы только для целочисленных полей; их ширина задаётся третьим элементом кортежей _fields_:

>>> class Int(Structure):
...     _fields_ = [("first_16", c_int, 16),
...                 ("second_16", c_int, 16)]
...
>>> print(Int.first_16)
<ctypes.CField 'first_16' type=c_int, ofs=0, bit_size=16, bit_offset=0>
>>> print(Int.second_16)
<ctypes.CField 'second_16' type=c_int, ofs=0, bit_size=16, bit_offset=16>

Важно отметить, что стандарт C не определяет распределение битовых полей и их расположение в памяти; реализация зависит от компилятора. По умолчанию Python пытается соответствовать поведению «родного» компилятора для текущей платформы. Подробности о поведении по умолчанию и его изменении см. в описании атрибута _layout_.

Массивы

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

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

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, original object return): при каждом получении атрибута создаётся новый эквивалентный объект:

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

Если присвоить атрибуту contents указателя другой экземпляр 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
>>>

Потокобезопасность без GIL

Начиная с Python 3.13, GIL можно отключить в сборке без GIL. В ctypes параллельное чтение и запись одного объекта безопасны, но работа с несколькими объектами одновременно — нет:

>>> number = c_int(42)
>>> pointer_a = pointer(number)
>>> pointer_b = pointer(number)

В приведённом выше примере при отключённом GIL безопасно, только если в каждый момент времени один объект читает данные по этому адресу или записывает их. Поэтому pointer_a можно совместно использовать и изменять из нескольких потоков, но только если pointer_b не пытается делать то же самое. Если это представляет проблему, рассмотрите возможность использования threading.Lock для синхронизации доступа к памяти:

>>> import threading
>>> lock = threading.Lock()
>>> # Thread 1
>>> with lock:
...    pointer_a.contents = 24
>>> # Thread 2
>>> with lock:
...    pointer_b.contents = 42

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

Обычно 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_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 не даёт ожидаемого результата.

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

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

>>> 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), чтобы найти файл библиотеки. Она возвращает имя файла библиотеки.

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

Изменено в версии 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 и Android функция 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() для поиска библиотеки во время выполнения.

Список загруженных разделяемых библиотек

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

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

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

>>> from ctypes.util import dllist
>>> dllist()
['', 'linux-vdso.so.1', '/lib/x86_64-linux-gnu/libm.so.6', '/lib/x86_64-linux-gnu/libc.so.6', ... ]

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

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

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

Представляет загруженную разделяемую библиотеку.

Функции этой библиотеки используют стандартное соглашение о вызовах C и считаются возвращающими int. Перед вызовом любой функции, экспортируемой этими библиотеками, глобальная блокировка интерпретатора Python global interpreter lock освобождается, а после вызова захватывается снова. Чтобы изменить поведение функций, используйте подкласс: OleDLL, WinDLL или PyDLL.

Если у вас есть существующий handle уже загруженной разделяемой библиотеки, его можно передать в качестве аргумента handle, чтобы обернуть открытую библиотеку в новый объект CDLL. В этом случае name используется только для задания атрибута _name, но это значение может быть изменено и/или проверено.

Если handle равен None, для загрузки библиотеки в процесс и получения дескриптора используется функция dlopen(3) или LoadLibrary() базовой платформы.

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

В системах, отличных от Windows, значением name может быть None. В этом случае вызывается dlopen() с аргументом NULL, который открывает основную программу как «библиотеку». (Некоторые системы делают то же самое, если name пусто; None/NULL — более переносимый вариант.)

Особенность реализации CPython

Поскольку CPython скомпонован с libc, для доступа к стандартной библиотеке C часто используется None name:

>>> printf = ctypes.CDLL(None).printf
>>> printf.argtypes = [ctypes.c_char_p]
>>> printf(b"hello\n")
hello
6

Для доступа к API Python на C предпочтительнее использовать ctypes.pythonapi, который работает на разных платформах.

Параметр 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() используются для получения и изменения частной копии ctypes кода ошибки Windows.

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

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

См. также

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

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

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

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

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

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

_handle

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

_name

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

class ctypes.OleDLL

Общие сведения см. в описании суперкласса CDLL.

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

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

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

class ctypes.WinDLL

Общие сведения см. в описании суперкласса CDLL.

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

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

class ctypes.PyDLL

Общие сведения см. в описании суперкласса CDLL.

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

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

ctypes.RTLD_GLOBAL

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

ctypes.RTLD_LOCAL

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

ctypes.DEFAULT_MODE

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

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

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

ctypes.pythonapi

Экземпляр PyDLL, предоставляющий функции API Python на C в виде атрибутов. Обратите внимание: предполагается, что все эти функции возвращают 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 — кортеж, содержащий параметры, изначально переданные при вызове функции. Это позволяет настроить поведение в зависимости от использованных аргументов.

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

В 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 — необязательный указатель на идентификатор интерфейса, используемый для расширенного формирования отчётов об ошибках.

Если iid не указан, при сбое вызова метода COM возникает исключение OSError. Если iid указан, вместо него возникает исключение COMError.

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

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

Необязательный параметр 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.CopyComPointer(src, dst)

Копирует указатель COM из src в dst и возвращает специфичное для Windows значение HRESULT.

Если src не является NULL, вызывается его метод AddRef, увеличивающий счётчик ссылок.

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

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

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

ctypes.cast(obj, type)

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

ctypes.create_string_buffer(init, size=None)
ctypes.create_string_buffer(size)

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

Если задан size (и он не равен None), это значение должно быть int. Оно задаёт размер возвращаемого массива.

Если задан аргумент init, он должен быть типа bytes. Он используется для инициализации элементов массива. Байты, не инициализированные таким способом, заполняются нулями (NUL).

Если size не задан (или равен None), размер буфера на один элемент больше, чем init, то есть добавляется завершающий NUL.

Если заданы оба аргумента, size не должен быть меньше len(init).

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

Если size равен len(init), завершающий NUL не добавляется. Не используйте такой буфер как строку C.

Например:

>>> bytes(create_string_buffer(2))
b'\x00\x00'
>>> bytes(create_string_buffer(b'ab'))
b'ab\x00'
>>> bytes(create_string_buffer(b'ab', 2))
b'ab'
>>> bytes(create_string_buffer(b'ab', 4))
b'ab\x00\x00'
>>> bytes(create_string_buffer(b'abcdef', 2))
Traceback (most recent call last):
   ...
ValueError: byte string too long

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

ctypes.create_unicode_buffer(init, size=None)
ctypes.create_unicode_buffer(size)

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

Функция принимает те же аргументы, что и create_string_buffer(), за исключением того, что init должен быть строкой, а size задаёт количество элементов c_wchar.

Вызывает событие аудита 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.util.dllist()

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

На большинстве платформ первый элемент списка обозначает текущий исполняемый файл. Это может быть пустая строка.

Доступность: Windows, macOS, iOS, glibc, BSD libc, musl

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

ctypes.FormatError([code])

Возвращает текстовое описание кода ошибки code. Если код ошибки не указан, используется последний код ошибки, полученный вызовом функции Windows API 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.

Особенность реализации CPython: Результирующий тип указателя кэшируется в атрибуте __pointer_type__ объекта type. До первого вызова POINTER этому атрибуту можно присвоить значение, чтобы задать пользовательский тип указателя. Однако делать это не рекомендуется: вручную создать подходящий тип указателя сложно, не полагаясь на особенности реализации, которые могут измениться в будущих версиях Python.

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)

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

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

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

ctypes.wstring_at(ptr, size=-1)

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

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

ctypes.memoryview_at(ptr, size, readonly=False)

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

Если readonly имеет значение true, возвращённый объект memoryview нельзя использовать для изменения базовой памяти. (Изменения, внесённые другими способами, по-прежнему будут отражаться в возвращённом объекте.)

Эта функция похожа на string_at(), но не копирует указанную область памяти. Она является семантически эквивалентной, но более эффективной альтернативой memoryview((c_byte * size).from_address(ptr)). (Хотя from_address() принимает только целые числа, ptr также может быть объектом типа ctypes.POINTER или byref().)

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

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

Типы данных

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:

__pointer_type__

Тип указателя, созданный вызовом POINTER() для соответствующего типа данных ctypes. Если тип указателя ещё не создан, атрибут отсутствует.

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

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

У каждого подкласса есть атрибут класса:

_type_

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

Типы, отмеченные в сводке звёздочкой (*), могут быть (или всегда являются) псевдонимами другого подкласса _SimpleCData и не обязательно используют указанный код типа. Например, если типы C long, long long и time_t на платформе совпадают, то c_long, c_longlong и c_time_t ссылаются на один класс — c_long, кодом _type_ которого является 'l'. Код 'L' использоваться не будет.

См. также

Модули array и struct, а также сторонние модули, например numpy, используют похожие, но немного отличающиеся коды типов.

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

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

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

class ctypes.c_float_complex

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

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

class ctypes.c_longdouble_complex

Представляет тип данных C long double complex, если он доступен. Конструктор принимает необязательное начальное значение complex.

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

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. Конструктор принимает необязательное целочисленное начальное значение; проверка переполнения не выполняется. На платформах, где sizeof(long long) == sizeof(long), этот тип является псевдонимом c_long.

class ctypes.c_short

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

class ctypes.c_size_t

Представляет тип данных C size_t. Обычно это псевдоним другого беззнакового целочисленного типа.

class ctypes.c_ssize_t

Представляет тип данных Py_ssize_t. Это знаковая версия size_t, то есть тип POSIX 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. Конструктор принимает необязательное целочисленное начальное значение; проверка переполнения не выполняется. На платформах, где sizeof(long long) == sizeof(long), этот тип является псевдонимом c_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*.

Изменено в версии 3.14: py_object теперь является обобщённым типом.

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

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

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

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

Объединения имеют общие атрибуты и поведение со структурами; подробности см. в документации Structure.

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_ можно задать только один раз. Последующие присваивания приведут к возникновению исключения AttributeError.

Кроме того, переменную класса _fields_ необходимо определить до первого использования типа структуры или объединения: создания экземпляра или подкласса, вызова для него sizeof() и т. д. Последующие присваивания _fields_ приведут к возникновению исключения AttributeError. Если _fields_ не была задана до такого использования, структура или объединение не будет иметь собственных полей, как если бы _fields_ была пустой.

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

_pack_

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

Реализовано только для совместимой с MSVC схемы размещения в памяти (см. _layout_).

Значение _pack_, равное 0, эквивалентно отсутствию этого атрибута. В противном случае значение должно быть положительной степенью двойки. Эффект эквивалентен #pragma pack(N) в C, за исключением того, что ctypes может допускать значения n больше тех, которые принимает компилятор.

_pack_ необходимо определить до присваивания _fields_, иначе оно не окажет никакого эффекта.

Устарело с версии 3.14, будет удалено в версии 3.19: По историческим причинам, если _pack_ не равно нулю, по умолчанию используется совместимая с MSVC схема размещения. На платформах, отличных от Windows, это значение по умолчанию устарело и, как планируется, в Python 3.19 будет приводить к ошибке. Если такое поведение необходимо, явно задайте для _layout_ значение 'ms'.

_align_

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

Значение не должно быть отрицательным. Эффект эквивалентен __attribute__((aligned(N))) в GCC или #pragma align(N) в MSVC, за исключением того, что ctypes может допускать значения, которые компилятор отклонил бы.

_align_ может только увеличивать требования к выравниванию структуры. Присваивание значения 0 или 1 не оказывает никакого эффекта.

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

_align_ необходимо определить до присваивания _fields_, иначе оно не окажет никакого эффекта.

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

_layout_

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

  • "ms": схема размещения, используемая компилятором Microsoft (MSVC). В GCC и Clang эту схему можно выбрать с помощью __attribute__((ms_struct)).
  • "gcc-sysv": схема размещения, используемая GCC с моделью данных System V или «подобной SysV», применяемой в Linux и macOS. При использовании этой схемы _pack_ должен быть не задан или равен нулю.

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

  • В Windows: "ms"
  • Если задано _pack_: "ms". (Это значение устарело; см. документацию _pack_.)
  • В остальных случаях: "gcc-sysv"

_layout_ необходимо определить до присваивания _fields_, иначе оно не окажет никакого эффекта.

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

_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.CField(*args, **kw)

Дескриптор полей Structure и Union. Например:

>>> class Color(Structure):
...     _fields_ = (
...         ('red', c_uint8),
...         ('green', c_uint8),
...         ('blue', c_uint8),
...         ('intense', c_bool, 1),
...         ('blinking', c_bool, 1),
...    )
...
>>> Color.red
<ctypes.CField 'red' type=c_ubyte, ofs=0, size=1>
>>> Color.green.type
<class 'ctypes.c_ubyte'>
>>> Color.blue.byte_offset
2
>>> Color.intense
<ctypes.CField 'intense' type=c_bool, ofs=3, bit_size=1, bit_offset=0>
>>> Color.blinking.bit_offset
1

Все атрибуты доступны только для чтения.

Объекты CField создаются с помощью _fields_; не создавайте экземпляры класса напрямую.

Добавлено в версии 3.14: Ранее у дескрипторов были только атрибуты offset и size, а также строковое представление, доступное для чтения; класс CField нельзя было использовать напрямую.

name

Имя поля в виде строки.

type

Тип поля в виде класса ctypes.

offset
byte_offset

Смещение поля в байтах.

Для битовых полей это смещение базовой выровненной по байтам единицы хранения; см. bit_offset.

byte_size

Размер поля в байтах.

Для битовых полей это размер базовой единицы хранения. Обычно он совпадает с размером типа битового поля.

size

Для не битовых полей эквивалентно byte_size.

Для битовых полей содержит упакованное в биты значение, сохранённое для обратной совместимости и объединяющее bit_size и bit_offset. Рекомендуется использовать отдельные атрибуты.

is_bitfield

True, если это битовое поле.

bit_offset
bit_size

Расположение битового поля в его единице хранения, то есть в byte_size байтах памяти, начиная со смещения byte_offset.

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

Для не битовых полей bit_offset равно нулю, а bit_size равно byte_size * 8.

is_anonymous

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

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

class ctypes.Array(*args)

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

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

Массивы являются обобщёнными по типу своих элементов.

_length_

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

_type_

Задаёт тип каждого элемента массива.

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

ctypes.ARRAY(type, length)

Создаёт массив. Эквивалентно type * length, где type — тип данных ctypes, а length — целое число.

Мягко объявлено устаревшим с версии 3.14: Рекомендуется использовать умножение.

class ctypes._Pointer

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

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

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

_type_

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

contents

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

Исключения

exception ctypes.ArgumentError

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

exception ctypes.COMError(hresult, text, details)

Это исключение возникает, если вызов метода COM завершается с ошибкой.

hresult

Целочисленное значение, представляющее код ошибки.

text

Сообщение об ошибке.

details

Кортеж из 5 элементов (descr, source, helpfile, helpcontext, progid).

descr — текстовое описание. source — зависящий от языка ProgID класса или приложения, вызвавшего ошибку. helpfile — путь к файлу справки. helpcontext — идентификатор раздела справки. progid — ProgID интерфейса, определившего ошибку.

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

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

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

Spec-Zone.ru

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