Документация Python неофициальный перевод
Содержание страницы

ctypes – библиотека для вызова внешних функций на Pythonctypes – A foreign function library for Python

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


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

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

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

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

Руководство по ctypesctypes tutorial

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

Доступ к функциям из загруженных DLLAccessing functions from loaded dlls

Функции доступны как атрибуты объектов 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 или string соответственно.

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

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

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

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

Вызов функцийCalling functions

Эти функции можно вызывать как любой другой вызываемый объект 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.

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

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

Тип ctypes

Тип C

Тип Python

_type_

c_bool

_Bool

bool

'?'

c_char

char

Односимвольный bytes

'c'

c_wchar

wchar_t

Односимвольный str

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

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

Тип 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)              # расположение в памяти изменилось
c_wchar_p(139966783348904)
>>> print(c_s.value)
Hi, there
>>> print(s)                # первый объект не изменился
Hello, World
>>>

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

>>> from ctypes import *
>>> p = create_string_buffer(3)            # создать буфер размером 3 байта, инициализированный нулевыми байтами
>>> print(sizeof(p), repr(p.raw))
3 b'\x00\x00\x00'
>>> p = create_string_buffer(b"Hello")     # создать буфер, содержащий строку, завершающуюся нулевым байтом
>>> print(sizeof(p), repr(p.raw))
6 b'Hello\x00'
>>> print(repr(p.value))
b'Hello'
>>> p = create_string_buffer(b"Hello", 10) # создать буфер на 10 байт
>>> print(sizeof(p), repr(p.raw))
10 b'Hello\x00\x00\x00\x00\x00'
>>> p.value = b"Hi"
>>> print(sizeof(p), repr(p.raw))
10 b'Hi\x00lo\x00\x00\x00\x00\x00'
>>>

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

Вызов функций, продолжениеCalling functions, continued

Обратите внимание: 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
>>>

Вызов функций с переменным числом аргументовCalling variadic functions

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

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

libc.printf.argtypes = [ctypes.c_char_p]

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

Вызов функций с собственными пользовательскими типами данныхCalling functions with your own custom data types

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

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

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

Указание требуемых типов аргументов (прототипы функций)Specifying the required argument types (function prototypes)

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

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

По умолчанию предполагается, что функции возвращают тип 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 – указатель на строку
>>> strchr(b"abcdef", ord("d"))
b'def'
>>> print(strchr(b"abcdef", ord("x")))
None
>>>

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

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

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

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

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

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

Передача указателей (или: передача параметров по ссылке)Passing pointers (or: passing parameters by reference)

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

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

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

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

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

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

Вот простой пример структуры POINT, которая содержит два целых числа с именами x и y, а также показывает, как инициализировать структуру в конструкторе:

>>> from ctypes import *
>>> class POINT(Structure):
...     _fields_ = [("x", c_int),
...                 ("y", c_int)]
...
>>> point = POINT(10, 20)
>>> print(point.x, point.y)
10 20
>>> point = POINT(y=5)
>>> print(point.x, point.y)
0 5
>>> POINT(1, 2, 3)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: too many initializers
>>>

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

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

Расположение структуры/объединения, выравнивание и порядок байтStructure/union layout, alignment and byte order

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

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

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

Битовые поля в структурах и объединенияхBit fields in structures and unions

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

МассивыArrays

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

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

TenPointsArrayType = POINT * 10

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

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

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

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

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

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

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

УказателиPointers

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

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

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

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

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

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

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

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

It is also possible to use indexes different from 0, but you must know what you’re doing, just as in C: You can access or change arbitrary memory locations. Generally you only use this feature if you receive a pointer from a C function, and you know that the pointer actually points to an array instead of a single item.

Под капотом функция 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
>>>

Потокобезопасность без GILThread safety without the GIL

Начиная с Python 3.13, 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()
>>> # Поток 1
>>> with lock:
...    pointer_a.contents = 24
>>> # Поток 2
>>> with lock:
...    pointer_b.contents = 42

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

Обычно 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
>>>

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

Неполные типы – это структуры, объединения или массивы, члены которых ещё не определены. В 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
>>>

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

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

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

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

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

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

Доступ к значениям, экспортируемым из DLLAccessing values exported from dlls

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

ctypes может получать доступ к таким значениям с помощью методов класса in_dll() соответствующего типа. pythonapi – это предопределенный символ, предоставляющий доступ к C API 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)),  # Указатель на функцию
...                ]
...
>>>

Мы определили тип данных _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__.

НеожиданностиSurprises

В 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
>>> # теперь поменять точки местами
>>> rc.a, rc.b = rc.b, rc.a
>>> print(rc.a.x, rc.a.y, rc.b.x, rc.b.y)
3 4 3 4
>>>

Хм. Мы, безусловно, ожидали, что последний оператор выведет 3 4 1 2. Что произошло? Вот шаги выполнения строки rc.a, rc.b = rc.b, rc.a выше:

>>> temp0, temp1 = rc.b, rc.a
>>> rc.a = temp0
>>> rc.b = temp1
>>>

Обратите внимание, что temp0 и temp1 – это объекты, все еще использующие внутренний буфер объекта rc выше. Таким образом, выполнение rc.a = temp0 копирует содержимое буфера temp0 в буфер rc. Это, в свою очередь, изменяет содержимое temp1. Поэтому последнее присваивание rc.b = temp1 не дает ожидаемого эффекта.

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

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

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

Примечание

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

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

Типы данных переменного размераVariable-sized data types

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

Справочник по ctypesctypes reference

Поиск разделяемых библиотекFinding shared libraries

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

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

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

ctypes.util.find_library(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() для поиска библиотеки во время выполнения.

Список загруженных динамических библиотекListing loaded shared libraries

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

Модуль 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', ... ]

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

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

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

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

Функции в этой библиотеке используют стандартное соглашение о вызовах C и предположительно возвращают int. Глобальная блокировка интерпретатора Python освобождается перед вызовом любой функции, экспортируемой этими библиотеками, и захватывается снова после. Для другого поведения функций используйте подкласс: 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, None с name часто используется для доступа к стандартной библиотеке C:

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

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

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

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

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

См. также

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

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

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

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

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

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

_handle

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

_name

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

class ctypes.OleDLL

См. CDLL, родительский класс, для общей информации.

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

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

class ctypes.WinDLL

См. CDLL, родительский класс, для общей информации.

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

class ctypes.PyDLL

См. CDLL, родительский класс, для общей информации.

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

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

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.

ctypes.oledll

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

ctypes.pydll

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

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

ctypes.pythonapi

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

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

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

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

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

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

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

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

ctypes.PYFUNCTYPE(restype, *argtypes)

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

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

prototype(address)

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

prototype(callable)

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

prototype(func_spec[, paramflags])

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

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

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

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

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

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

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

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

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

1

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

2

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

4

Входной параметр, по умолчанию равный нулю.

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

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

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

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

Вот обёртка с помощью ctypes:

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

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

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

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

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

Вот обёртка с помощью ctypes:

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

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

Выходные параметры можно комбинировать с протоколом errcheck для дополнительной обработки выходных данных и проверки ошибок. Функция win32 API 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
>>>

Вспомогательные функцииUtility functions

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 и возвращает значение HRESULT, специфичное для Windows.

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

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

Добавлено в версии 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, экспортируемой DLL расширения _ctypes.

ctypes.DllGetClassObject()

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

ctypes.util.find_library(name)

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

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

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

ctypes.util.find_msvcrt()

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

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

ctypes.util.dllist()

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

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

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

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

ctypes.FormatError([code])

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

ctypes.GetLastError()

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

ctypes.get_errno()

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

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

ctypes.get_last_error()

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

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

ctypes.memmove(dst, src, count)

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

ctypes.memset(dst, c, count)

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

ctypes.POINTER(type, /)

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

Детали реализации 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)

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

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

ctypes.set_last_error(value)

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

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

Изменено в версии 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.

Типы данныхData types

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

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

class ctypes._SimpleCData

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

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

value

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

При получении атрибута 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, вы всегда получите объект Python bytes, а не экземпляр c_char_p.

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

Вот фундаментальные типы данных ctypes:

class ctypes.c_byte

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

class ctypes.c_char

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

class ctypes.c_char_p

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

class ctypes.c_double

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

class ctypes.c_longdouble

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

class ctypes.c_float

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

class ctypes.c_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, которое содержит информацию об успехе или ошибке для вызова функции или метода.

class ctypes.py_object

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

_fields_

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

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

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

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

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

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

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

ИсключенияExceptions

exception ctypes.ArgumentError

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

exception ctypes.COMError(hresult, text, details)

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

hresult

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

text

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

details

5-кортеж (descr, source, helpfile, helpcontext, progid).

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

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