ctypes — Библиотека внешних функций для Python
ctypes — это библиотека внешних функций для Python. Она предоставляет совместимые с C типы данных и позволяет вызывать функции в DLL или динамических библиотеках. Её можно использовать для оборачивания этих библиотек чистым Python.
1. Руководство по использованию ctypes
Примечание: Примеры кода в этом руководстве используют doctest для проверки их работоспособности. Поскольку некоторые примеры кода ведут себя по-разному в Linux, Windows или macOS, они содержат директивы doctest в комментариях.
Примечание: Некоторые примеры кода ссылаются на тип ctypes c_int. На платформах, где sizeof(long) == sizeof(int) он является псевдонимом для c_long. Поэтому не стоит путаться, если c_long выводится, когда ожидается c_int — на самом деле они являются одним и тем же типом.
1.1. Загрузка динамических библиотек
ctypes экспортирует объекты cdll, а в Windows — windll и oledll для загрузки динамических библиотек.
Библиотеки загружаются через доступ к ним как к атрибутам этих объектов. cdll загружает библиотеки, экспортирующие функции с использованием стандартной cdecl конвенции вызова, в то время как windll загружает библиотеки, вызывающие функции с использованием stdcall конвенции вызова. oledll также использует stdcall конвенцию вызова и предполагает, что функции возвращают код ошибки Windows HRESULT. Код ошибки используется для автоматического поднятия исключения OSError при неудачном вызове функции.
Изменено в версии 3.3: Ошибки Windows раньше поднимали исключение WindowsError, которое сейчас является псевдонимом для OSError.
Вот несколько примеров для Windows. Обратите внимание, что msvcrt — это стандартная библиотека MS C, содержащая большинство стандартных функций C и использующая конвенцию вызова cdecl:
>>> from ctypes import * >>> print(windll.kernel32) <WinDLL 'kernel32', handle ... at ...> >>> print(cdll.msvcrt) <CDLL 'msvcrt', handle ... at ...> >>> libc = cdll.msvcrt >>>
Windows автоматически добавляет обычное .dll расширение файла.
Примечание
Обращение к стандартной библиотеке C через cdll.msvcrt может использовать устаревшую версию библиотеки, которая может быть несовместима с той, которая используется Python. В случае возможности используйте родные функции Python, или же импортируйте и используйте модуль msvcrt.
В Linux необходимо указывать имя файла включая расширение для загрузки библиотеки, поэтому доступ через атрибуты для загрузки библиотек недоступен. Должен использоваться метод LoadLibrary() загрузчиков DLL, или же библиотека должна загружаться путём создания экземпляра CDLL с вызовом конструктора:
>>> cdll.LoadLibrary("libc.so.6")
<CDLL 'libc.so.6', handle ... at ...>
>>> libc = CDLL("libc.so.6")
>>> libc
<CDLL 'libc.so.6', handle ... at ...>
>>>
1.2. Доступ к функциям из загруженных DLL
Функции доступны как атрибуты объектов DLL:
>>> from ctypes import *
>>> libc.printf
<_FuncPtr object at 0x...>
>>> print(windll.kernel32.GetModuleHandleA)
<_FuncPtr object at 0x...>
>>> print(windll.kernel32.MyOwnFunction)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "ctypes.py", line 239, in __getattr__
func = _StdcallFuncPtr(name, self)
AttributeError: function 'MyOwnFunction' not found
>>>
Обратите внимание, что системные DLL win32, такие как kernel32 и user32 часто экспортируют версии функций ANSI и UNICODE. Версия UNICODE экспортируется с приставкой W, в то время как версия ANSI экспортируется с приставкой A к имени. Функция win32 GetModuleHandle, которая возвращает дескриптор модуля для данного имени модуля, имеет следующий прототип C, и используется макрос, чтобы экспонировать один из них как GetModuleHandle в зависимости от того, определён UNICODE или нет:
/* ANSI version */ HMODULE GetModuleHandleA(LPCSTR lpModuleName); /* UNICODE version */ HMODULE GetModuleHandleW(LPCWSTR lpModuleName);
windll не пытается магическим образом выбрать один из них, вы должны получить нужную версию явно, указав GetModuleHandleA или GetModuleHandleW явно, а затем вызвать её с байтовыми или строковыми объектами соответственно.
Иногда DLL экспортируют функции с именами, которые не являются допустимыми идентификаторами Python, такими как "??2@YAPAXI@Z". В этом случае необходимо использовать getattr() для получения функции:
>>> getattr(cdll.msvcrt, "??2@YAPAXI@Z") <_FuncPtr object at 0x...> >>>
В Windows некоторые DLL экспортируют функции не по имени, а по порядковому номеру. К этим функциям можно получить доступ, индексируя объект DLL порядковым номером:
>>> cdll.kernel32[1]
<_FuncPtr object at 0x...>
>>> cdll.kernel32[0]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "ctypes.py", line 310, in __getitem__
func = _StdcallFuncPtr(name, self)
AttributeError: function ordinal 0 not found
>>>
1.3. Вызов функций
Эти функции можно вызывать как любые другие вызываемые объекты Python. Этот пример использует функцию time(), которая возвращает системное время в секундах с момента эпохи Unix, и функцию GetModuleHandleA(), которая возвращает дескриптор модуля win32.
В этом примере обе функции вызываются с указателем NULL (None должен использоваться в качестве указателя NULL):
>>> print(libc.time(None)) 1150640792 >>> print(hex(windll.kernel32.GetModuleHandleA(None))) 0x1d000000 >>>
ctypes пытается защитить вас от вызова функций с неправильным числом аргументов или неправильной конвенцией вызова. К сожалению, это работает только в Windows. Это делается путём проверки стека после возвращения из функции, поэтому, хотя и генерируется ошибка, функция вызывается:
>>> windll.kernel32.GetModuleHandleA() Traceback (most recent call last): File "<stdin>", line 1, in <module> ValueError: Procedure probably called with not enough arguments (4 bytes missing) >>> windll.kernel32.GetModuleHandleA(0, 0) Traceback (most recent call last): File "<stdin>", line 1, in <module> ValueError: Procedure probably called with too many arguments (4 bytes in excess) >>>
Такое же исключение поднимается, когда вы вызываете stdcall функцию с cdecl конвенцией вызова, или наоборот:
>>> cdll.kernel32.GetModuleHandleA(None) Traceback (most recent call last): File "<stdin>", line 1, in <module> ValueError: Procedure probably called with not enough arguments (4 bytes missing) >>> >>> windll.msvcrt.printf(b"spam") Traceback (most recent call last): File "<stdin>", line 1, in <module> ValueError: Procedure probably called with too many arguments (4 bytes in excess) >>>
Для определения правильной конвенции вызова необходимо обратиться к заголовочному файлу C или документации функции, которую вы хотите вызвать.
В Windows, ctypes использует обработку структурных исключений win32 для предотвращения сбоев из-за общих защитных ошибок при вызове функций с недопустимыми значениями аргументов:
>>> windll.kernel32.GetModuleHandleA(32) Traceback (most recent call last): File "<stdin>", line 1, in <module> OSError: exception: access violation reading 0x00000020 >>>
Однако, есть достаточно способов сломать Python с помощью ctypes, поэтому вы должны быть осторожны. Модуль faulthandler может быть полезен при отладке сбоев (например, при сегментационных ошибках, вызванных ошибочными вызовами функций из библиотек C).
None, целые числа, байтовые объекты и (строки) являются единственными встроенными объектами Python, которые могут быть напрямую использованы в качестве параметров в этих вызовах функций. None передаётся как C-указатель NULL, байтовые объекты и строки передаются как указатели на блок памяти, содержащий их данные (char * или wchar_t *). Целые числа Python передаются как тип C по умолчанию для платформы, их значение маскируется для соответствия типу C.
Прежде чем перейти к вызову функций с другими типами параметров, мы должны узнать больше о типах данных ctypes.
1.4. Основные типы данных
ctypes определяет ряд примитивных типов данных, совместимых с C:
| Тип ctypes | Тип C | Тип Python |
|---|---|---|
c_bool | _Bool | bool (1) |
c_char | char | объект байтов из 1 символа |
c_wchar | wchar_t | строка из 1 символа |
c_byte | char | целое число |
c_ubyte | unsigned char | целое число |
c_short | short | целое число |
c_ushort | unsigned short | целое число |
c_int | int | целое число |
c_uint | unsigned int | целое число |
c_long | long | целое число |
c_ulong | unsigned long | целое число |
c_longlong |
__int64 или long long
| целое число |
c_ulonglong |
unsigned __int64 или unsigned long long
| целое число |
c_size_t | size_t | целое число |
c_ssize_t |
ssize_t или Py_ssize_t
| целое число |
c_float | float | float |
c_double | double | float |
c_longdouble | long double | float |
c_char_p |
char * (с завершением NUL) | объект bytes или None
|
c_wchar_p |
wchar_t * (с завершением NUL) | строка или None
|
c_void_p | void * | целое число или None
|
- Конструктор принимает любой объект с логическим значением.
Все эти типы можно создать, вызвав их с необязательным инициализатором соответствующего типа и значения:
>>> c_int()
c_long(0)
>>> c_wchar_p("Hello, World")
c_wchar_p(140018365411392)
>>> c_ushort(-3)
c_ushort(65533)
>>>
Поскольку эти типы изменяемы, их значение также можно изменить позже:
>>> i = c_int(42) >>> print(i) c_long(42) >>> print(i.value) 42 >>> i.value = -99 >>> print(i.value) -99 >>>
Присвоение нового значения экземплярам типов указателей c_char_p, c_wchar_p и c_void_p изменяет местоположение в памяти, на которое они указывают, а не содержимое блока памяти (разумеется, потому что объекты Python bytes неизменяемы):
>>> s = "Hello, World" >>> c_s = c_wchar_p(s) >>> print(c_s) c_wchar_p(139966785747344) >>> print(c_s.value) Hello World >>> c_s.value = "Hi, there" >>> print(c_s) # the memory location has changed c_wchar_p(139966783348904) >>> print(c_s.value) Hi, there >>> print(s) # first object is unchanged Hello, World >>>
Однако необходимо быть осторожным, чтобы не передавать их функциям, ожидающим указателей на изменяемую память. Если вам нужны изменяемые блоки памяти, ctypes имеет функцию create_string_buffer(), которая создаёт их различными способами. Текущее содержимое блока памяти можно получить (или изменить) с помощью свойства raw; если вы хотите получить доступ к нему как к строке с завершением NUL, используйте свойство value:
>>> from ctypes import * >>> p = create_string_buffer(3) # create a 3 byte buffer, initialized to NUL bytes >>> print(sizeof(p), repr(p.raw)) 3 b'\x00\x00\x00' >>> p = create_string_buffer(b"Hello") # create a buffer containing a NUL terminated string >>> print(sizeof(p), repr(p.raw)) 6 b'Hello\x00' >>> print(repr(p.value)) b'Hello' >>> p = create_string_buffer(b"Hello", 10) # create a 10 byte buffer >>> print(sizeof(p), repr(p.raw)) 10 b'Hello\x00\x00\x00\x00\x00' >>> p.value = b"Hi" >>> print(sizeof(p), repr(p.raw)) 10 b'Hi\x00lo\x00\x00\x00\x00\x00' >>>
Функция create_string_buffer() заменяет функцию c_buffer() (которая всё ещё доступна как псевдоним), а также функцию c_string() из более ранних выпусков ctypes. Чтобы создать изменяемый блок памяти, содержащий символы Unicode типа C wchar_t, используйте функцию create_unicode_buffer().
1.5. Продолжение вызова функций
Обратите внимание, что printf выводит данные в реальный канал стандартного вывода, а не в sys.stdout, поэтому эти примеры будут работать только в командной строке, а не изнутри IDLE или PythonWin:
>>> printf = libc.printf >>> printf(b"Hello, %s\n", b"World!") Hello, World! 14 >>> printf(b"Hello, %S\n", "World!") Hello, World! 14 >>> printf(b"%d bottles of beer\n", 42) 42 bottles of beer 19 >>> printf(b"%f bottles of beer\n", 42.5) Traceback (most recent call last): File "<stdin>", line 1, in <module> ArgumentError: argument 2: exceptions.TypeError: Don't know how to convert parameter 2 >>>
Как уже упоминалось ранее, все типы Python, кроме целых чисел, строк и объектов bytes, должны быть обернуты в соответствующий тип ctypes, чтобы их можно было преобразовать в требуемый тип данных C:
>>> printf(b"An int %d, a double %f\n", 1234, c_double(3.14)) An int 1234, a double 3.140000 31 >>>
1.6. Вызов функций со своими собственными пользовательскими типами данных
Вы также можете настроить преобразование аргументов ctypes, чтобы позволить использовать экземпляры собственных классов в качестве аргументов функций. ctypes ищет атрибут _as_parameter_ и использует его в качестве аргумента функции. Конечно, он должен быть целым числом, строкой или объектом bytes:
>>> 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, которое делает атрибут доступным по запросу.
1.7. Указание требуемых типов аргументов (прототипы функций)
Можно указать требуемые типы аргументов функций, экспортируемых из DLL, установив атрибут argtypes.
argtypes должен быть последовательностью типов данных C (функция printf — вероятно, не хороший пример здесь, потому что она принимает переменное количество и различные типы параметров в зависимости от строки формата, с другой стороны, это довольно удобно для экспериментов с этой функцией):
>>> printf.argtypes = [c_char_p, c_char_p, c_int, c_double] >>> printf(b"String '%s', Int %d, Double %f\n", b"Hi", 10, 2.2) String 'Hi', Int 10, Double 2.200000 37 >>>
Указание формата защищает от несовместимых типов аргументов (как прототип функции C) и пытается преобразовать аргументы в допустимые типы:
>>> printf(b"%d %d %d", 1, 2, 3) Traceback (most recent call last): File "<stdin>", line 1, in <module> ArgumentError: argument 2: exceptions.TypeError: wrong type >>> printf(b"%s %d %f\n", b"X", 2, 3) X 2 3.000000 13 >>>
Если вы определили собственные классы, которые передаёте в вызовы функций, вам нужно реализовать метод класса from_param(), чтобы использовать их в последовательности argtypes. Метод класса from_param() получает объект Python, переданный в вызов функции, он должен выполнить проверку типов или другие необходимые действия, чтобы убедиться, что этот объект приемлем, а затем вернуть сам объект, его атрибут _as_parameter_ или то, что вы хотите передать в качестве аргумента функции C в этом случае. Опять же, результатом должны быть целое число, строка, объект bytes, экземпляр ctypes или объект с атрибутом _as_parameter_.
1.8. Типы возвращаемых значений
По умолчанию предполагается, что функции возвращают тип C int. Другие типы возвращаемых значений можно указать, установив атрибут restype объекта функции.
Вот более продвинутый пример, он использует функцию strchr, которая ожидает указатель на строку и символ и возвращает указатель на строку:
>>> strchr = libc.strchr
>>> strchr(b"abcdef", ord("d"))
8059983
>>> strchr.restype = c_char_p # c_char_p is a pointer to a string
>>> strchr(b"abcdef", ord("d"))
b'def'
>>> print(strchr(b"abcdef", ord("x")))
None
>>>
Если вы хотите избежать вызовов ord("x") выше, вы можете установить атрибут argtypes, и второй аргумент будет преобразован из объекта Python bytes, содержащего один символ, в символ C:
>>> strchr.restype = c_char_p >>> strchr.argtypes = [c_char_p, c_char] >>> strchr(b"abcdef", b"d") 'def' >>> strchr(b"abcdef", b"def") Traceback (most recent call last): File "<stdin>", line 1, in <module> ArgumentError: argument 2: exceptions.TypeError: one character string expected >>> print(strchr(b"abcdef", b"x")) None >>> strchr(b"abcdef", b"d") 'def' >>>
Вы также можете использовать вызываемый объект Python (функцию или класс, например) в качестве атрибута restype, если внешняя функция возвращает целое число. Вызываемый объект будет вызван с целым числом, которое возвращает функция C, а результат этого вызова будет использоваться как результат вашего вызова функции. Это полезно для проверки кодов возврата ошибок и автоматического повышения исключений:
>>> GetModuleHandle = windll.kernel32.GetModuleHandleA
>>> def ValidHandle(value):
... if value == 0:
... raise WinError()
... return value
...
>>>
>>> GetModuleHandle.restype = ValidHandle
>>> GetModuleHandle(None)
486539264
>>> GetModuleHandle("something silly")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in ValidHandle
OSError: [Errno 126] The specified module could not be found.
>>>
WinError — это функция, которая будет вызывать Windows API FormatMessage() для получения строкового представления кода ошибки и возвращает исключение. WinError принимает необязательный параметр кода ошибки; если он не указан, она вызывает GetLastError() для его извлечения.
Обратите внимание, что механизм проверки ошибок намного более мощный благодаря атрибуту errcheck; см. справочное руководство для получения дополнительных сведений.
1.9. Передача указателей (или: передача параметров по ссылке)
Иногда функция API C ожидает указатель на тип данных в качестве параметра, вероятно, чтобы записать в соответствующее местоположение, или если данные слишком велики, чтобы передавать их по значению. Это также известно как передача параметров по ссылке.
ctypes экспортирует функцию byref(), которая используется для передачи параметров по ссылке. Тот же эффект можно получить с помощью функции pointer(), хотя pointer() выполняет гораздо больше работы, так как создаёт настоящий объект указателя, поэтому быстрее использовать byref(), если вам не нужен сам объект указателя в Python:
>>> i = c_int() >>> f = c_float() >>> s = create_string_buffer(b'\000' * 32) >>> print(i.value, f.value, repr(s.value)) 0 0.0 b'' >>> libc.sscanf(b"1 3.14 Hello", b"%d %f %s", ... byref(i), byref(f), s) 3 >>> print(i.value, f.value, repr(s.value)) 1 3.1400001049 b'Hello' >>>
1.10. Структуры и объединения
Структуры и объединения должны быть производными от базовых классов 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>
ValueError: too many initializers
>>>
Однако вы можете создавать гораздо более сложные структуры. Структура сама может содержать другие структуры, используя структуру как тип поля.
Вот структура RECT, которая содержит два объекта POINT с именами *upperleft* и *lowerright*:
>>> class RECT(Structure):
... _fields_ = [("upperleft", POINT),
... ("lowerright", POINT)]
...
>>> rc = RECT(point)
>>> print(rc.upperleft.x, rc.upperleft.y)
0 5
>>> print(rc.lowerright.x, rc.lowerright.y)
0 0
>>>
Вложенные структуры также можно инициализировать в конструкторе несколькими способами:
>>> r = RECT(POINT(1, 2), POINT(3, 4)) >>> r = RECT((1, 2), (3, 4))
Поля дескрипторы могут быть получены из *класса*, они полезны для отладки, так как могут предоставить полезную информацию:
>>> print(POINT.x) <Field type=c_long, ofs=0, size=4> >>> print(POINT.y) <Field type=c_long, ofs=4, size=4> >>>
Предупреждение
ctypes не поддерживает передачу объединений или структур с битами полей в функции по значению. Хотя это может работать на 32-битном x86, это не гарантируется библиотекой в общем случае. Объединения и структуры с битами полей всегда должны передаваться функциям по указателю.
1.11. Выравнивание и порядок байтов структур/объединений
По умолчанию поля Структуры и Объединения выравниваются так же, как это делает компилятор C. Можно переопределить это поведение, указав атрибут _pack_ в определении подкласса. Это должно быть положительное целое число и определяет максимальное выравнивание для полей. Это то, что #pragma pack(n) также делает в MSVC.
ctypes использует собственный порядок байтов для Структур и Объединений. Для построения структур с нестандартным порядком байтов можно использовать один из базовых классов BigEndianStructure, LittleEndianStructure, BigEndianUnion, и LittleEndianUnion. Эти классы не могут содержать поля указателей.
1.12. Битовые поля в структурах и объединениях
Можно создавать структуры и объединения, содержащие битовые поля. Битовые поля возможны только для целочисленных полей, ширина бита указывается как третий элемент в кортежах _fields_:
>>> class Int(Structure):
... _fields_ = [("first_16", c_int, 16),
... ("second_16", c_int, 16)]
...
>>> print(Int.first_16)
<Field type=c_long, ofs=0:0, bits=16>
>>> print(Int.second_16)
<Field type=c_long, ofs=0:16, bits=16>
>>>
1.13. Массивы
Массивы — это последовательности, содержащие фиксированное число экземпляров одного и того же типа.
Рекомендуемый способ создания типов массивов — умножить тип данных на положительное целое число:
TenPointsArrayType = POINT * 10
Вот пример несколько искусственного типа данных, структуры, содержащей 4 объекта POINT среди прочего:
>>> from ctypes import *
>>> class POINT(Structure):
... _fields_ = ("x", c_int), ("y", c_int)
...
>>> class MyStruct(Structure):
... _fields_ = [("a", c_int),
... ("b", c_float),
... ("point_array", POINT * 4)]
>>>
>>> print(len(MyStruct().point_array))
4
>>>
Экземпляры создаются обычным способом, вызывая класс:
arr = TenPointsArrayType()
for pt in arr:
print(pt.x, pt.y)
Приведённый выше код печатает ряд 0 0 строк, потому что содержимое массива инициализировано нулями.
Также можно указать инициализаторы правильного типа:
>>> from ctypes import * >>> TenIntegers = c_int * 10 >>> ii = TenIntegers(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) >>> print(ii) <c_long_Array_10 object at 0x...> >>> for i in ii: print(i, end=" ") ... 1 2 3 4 5 6 7 8 9 10 >>>
1.14. Указатели
Экземпляры указателей создаются путём вызова функции pointer() над типом ctypes:
>>> from ctypes import * >>> i = c_int(42) >>> pi = pointer(i) >>>
Экземпляры указателей имеют атрибут contents, который возвращает объект, на который указывает указатель, объект i выше:
>>> pi.contents c_long(42) >>>
Обратите внимание, что ctypes не имеет OOR (возврат исходного объекта), он каждый раз создаёт новый эквивалентный объект при получении атрибута:
>>> pi.contents is i False >>> pi.contents is pi.contents False >>>
Присвоение другого экземпляра c_int атрибуту содержимого указателя приведет к тому, что указатель будет указывать на место в памяти, где это хранится:
>>> i = c_int(99) >>> pi.contents = i >>> pi.contents c_long(99) >>>
Экземпляры указателей также можно индексировать целыми числами:
>>> pi[0] 99 >>>
Присвоение целочисленному индексу изменяет значение, на которое указывает указатель:
>>> print(i) c_long(99) >>> pi[0] = 22 >>> print(i) c_long(22) >>>
Также можно использовать индексы, отличные от 0, но вы должны понимать, что делаете, так же как и в C: Вы можете обращаться к произвольным местам памяти или изменять их. Обычно вы используете эту функцию только если получаете указатель из функции C и *знаете*, что указатель фактически указывает на массив, а не на единственный элемент.
За кулисами функция pointer() делает больше, чем просто создаёт экземпляры указателей, она должна сначала создать типы указателей. Это делается с помощью функции POINTER(), которая принимает любой тип ctypes и возвращает новый тип:
>>> PI = POINTER(c_int) >>> PI <class 'ctypes.LP_c_long'> >>> PI(42) Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: expected c_long instead of int >>> PI(c_int(42)) <ctypes.LP_c_long object at 0x...> >>>
Вызов типа указателя без аргумента создаёт NULL указатель. NULL указатели имеют булево значение False:
>>> null_ptr = POINTER(c_int)() >>> print(bool(null_ptr)) False >>>
ctypes проверяет NULL при разыменовании указателей (но разыменование недействительных не-NULL указателей приведёт к аварийному завершению Python):
>>> null_ptr[0]
Traceback (most recent call last):
....
ValueError: NULL pointer access
>>>
>>> null_ptr[0] = 1234
Traceback (most recent call last):
....
ValueError: NULL pointer access
>>>
1.15. Преобразования типов
Обычно 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 >>>
1.16. Неполные типы
Неполные типы — это структуры, объединения или массивы, члены которых ещё не указаны. В 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 = "foo" >>> c2 = cell() >>> c2.name = "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 >>>
1.17. Функции обратного вызова
ctypes позволяет создавать указатели на функции, вызываемые из C, из вызываемых функций Python. Иногда их называют функциями обратного вызова.
Сначала необходимо создать класс для функции обратного вызова. Класс знает соглашение о вызове, тип возвращаемого значения и количество и типы аргументов, которые получит эта функция.
Функция-фабрика CFUNCTYPE() создаёт типы для функций обратного вызова, используя соглашение о вызове cdecl. В Windows функция-фабрика WINFUNCTYPE() создаёт типы для функций обратного вызова, используя соглашение о вызове stdcall.
Обе эти функции-фабрики вызываются с типом результата в качестве первого аргумента и ожидаемыми типами аргументов функций обратного вызова в качестве остальных аргументов.
Здесь я приведу пример, который использует стандартную функцию библиотеки C qsort(), которая используется для сортировки элементов с помощью функции обратного вызова. qsort() будет использоваться для сортировки массива целых чисел:
>>> IntArray5 = c_int * 5 >>> ia = IntArray5(5, 1, 7, 33, 99) >>> qsort = libc.qsort >>> qsort.restype = None >>>
qsort() необходимо вызвать с указателем на данные для сортировки, количеством элементов в массиве данных, размером одного элемента и указателем на функцию сравнения — функцию обратного вызова. Функция обратного вызова затем будет вызвана с двумя указателями на элементы, и она должна вернуть целое отрицательное число, если первый элемент меньше второго, ноль, если они равны, и целое положительное число в противном случае.
Таким образом, наша функция обратного вызова получает указатели на целые числа и должна вернуть целое число. Сначала мы создаем type для функции обратного вызова:
>>> CMPFUNC = CFUNCTYPE(c_int, POINTER(c_int), POINTER(c_int)) >>>
Для начала вот простой обратный вызов, демонстрирующий значения, которые он получает:
>>> def py_cmp_func(a, b):
... print("py_cmp_func", a[0], b[0])
... return 0
...
>>> cmp_func = CMPFUNC(py_cmp_func)
>>>
Результат:
>>> qsort(ia, len(ia), sizeof(c_int), cmp_func) py_cmp_func 5 1 py_cmp_func 33 99 py_cmp_func 7 33 py_cmp_func 5 7 py_cmp_func 1 7 >>>
Теперь мы можем фактически сравнить два элемента и вернуть полезный результат:
>>> def py_cmp_func(a, b):
... print("py_cmp_func", a[0], b[0])
... return a[0] - b[0]
...
>>>
>>> qsort(ia, len(ia), sizeof(c_int), CMPFUNC(py_cmp_func))
py_cmp_func 5 1
py_cmp_func 33 99
py_cmp_func 7 33
py_cmp_func 1 7
py_cmp_func 5 7
>>>
Как легко проверить, наш массив теперь отсортирован:
>>> for i in ia: print(i, end=" ") ... 1 5 7 33 99 >>>
Примечание
Убедитесь, что вы сохраняете ссылки на объекты CFUNCTYPE() до тех пор, пока они используются из кода C. ctypes этого не делает, и если вы этого не сделаете, они могут быть собраны сборщиком мусора, что приведёт к сбою вашей программы при вызове функции обратного вызова.
Обратите также внимание, что если функция обратного вызова вызывается в потоке, созданном за пределами управления Python (например, внешним кодом, вызывающим обратный вызов), ctypes создаёт новый фиктивный поток Python при каждом вызове. Это поведение верно для большинства целей, но это означает, что значения, хранящиеся с помощью threading.local, не сохранятся между различными вызовами обратного вызова, даже если эти вызовы выполняются из одного и того же потока C.
1.18. Доступ к значениям, экспортируемым из DLL
Некоторые общие библиотеки не только экспортируют функции, но и экспортируют переменные. Примером в самой библиотеке Python является Py_OptimizeFlag, целое число, установленное в 0, 1 или 2 в зависимости от флага -O или -OO, заданного при запуске.
ctypes может получать доступ к таким значениям с помощью методов класса in_dll() типа. pythonapi — это предопределённый символ, обеспечивающий доступ к API Python на C:
>>> opt_flag = c_int.in_dll(pythonapi, "Py_OptimizeFlag") >>> print(opt_flag) c_long(0) >>>
Если бы интерпретатор был запущен с -O, образец напечатал бы c_long(1), или c_long(2) если бы был указан -OO.
Расширенный пример, который также демонстрирует использование указателей, получает доступ к указателю PyImport_FrozenModules, экспортируемому Python.
Цитата из документации для этого значения:
Этот указатель инициализируется, чтобы указывать на массивstruct _frozen записей, завершаемых одной, члены которой все NULL или ноль. При импорте замороженного модуля он ищется в этой таблице. Внешний код может хитрить с этим, чтобы предоставить динамически созданный набор замороженных модулей. Так что манипулирование этим указателем может даже оказаться полезным. Чтобы ограничить размер примера, мы покажем только то, как эту таблицу можно прочитать с помощью ctypes:
>>> from ctypes import *
>>>
>>> class struct_frozen(Structure):
... _fields_ = [("name", c_char_p),
... ("code", POINTER(c_ubyte)),
... ("size", c_int)]
...
>>>
Мы определили тип данных struct _frozen, поэтому мы можем получить указатель на таблицу:
>>> FrozenTable = POINTER(struct_frozen) >>> table = FrozenTable.in_dll(pythonapi, "PyImport_FrozenModules") >>>
Поскольку table является 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
__hello__ 161
__phello__ -161
__phello__.spam 161
>>>
Тот факт, что стандартный Python имеет замороженный модуль и замороченный пакет (указанный отрицательным членом размера), не очень известен, он используется только для тестирования. Попробуйте использовать import __hello__, например.
1.19. Неожиданности
В ctypes есть некоторые граничные случаи, где вы можете ожидать чего-то другого, чем то, что происходит на самом деле.
Рассмотрим следующий пример:
>>> from ctypes import *
>>> class POINT(Structure):
... _fields_ = ("x", c_int), ("y", c_int)
...
>>> class RECT(Structure):
... _fields_ = ("a", POINT), ("b", POINT)
...
>>> p1 = POINT(1, 2)
>>> p2 = POINT(3, 4)
>>> rc = RECT(p1, p2)
>>> print(rc.a.x, rc.a.y, rc.b.x, rc.b.y)
1 2 3 4
>>> # now swap the two points
>>> rc.a, rc.b = rc.b, rc.a
>>> print(rc.a.x, rc.a.y, rc.b.x, rc.b.y)
3 4 3 4
>>>
Хм. Мы определённо ожидали, что последнее утверждение напечатает 3 4 1 2. Что случилось? Вот шаги в строке rc.a, rc.b = rc.b, rc.a выше:
>>> temp0, temp1 = rc.b, rc.a >>> rc.a = temp0 >>> rc.b = temp1 >>>
Обратите внимание, что temp0 и temp1 по-прежнему используют внутренний буфер объекта rc выше. Таким образом, выполнение rc.a = temp0 копирует содержимое буфера temp0 в буфер rc. Это, в свою очередь, изменяет содержимое temp1. Поэтому последнее присваивание rc.b = temp1, не имеет ожидаемого эффекта.
Помните, что извлечение подобъектов из структур, объединений и массивов не копирует подобъект, а возвращает оберточный объект, обращающийся к базовому буферу исходного объекта.
Ещё один пример, который может вести себя иначе, чем можно было бы ожидать:
>>> s = c_char_p() >>> s.value = "abc def ghi" >>> s.value 'abc def ghi' >>> s.value is s.value False >>>
Почему он печатает False? Экземпляры ctypes — это объекты, содержащие блок памяти плюс некоторые описатели, обращающиеся к содержимому памяти. Хранение объекта Python в блоке памяти не сохраняет сам объект, а вместо этого сохраняет contents объекта. Доступ к содержимому снова создаёт новый объект Python каждый раз!
1.20. Типы данных с переменным размером
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 и (пере)определять тип данных после того, как требуемый размер уже известен, в каждом конкретном случае.
2. Справочник по ctypes
2.1. Поиск общих библиотек
При программировании на языке с компиляцией, общие библиотеки используются при компиляции/связывании программы и при её запуске.
Цель функции find_library() — найти библиотеку способом, аналогичным тому, как это делает компилятор (на платформах с несколькими версиями общей библиотеки должна загружаться самая последняя), в то время как загрузчики ctypes ведут себя, как при запуске программы, и вызывают загрузчик времени выполнения напрямую.
Модуль ctypes.util предоставляет функцию, которая может помочь определить библиотеку для загрузки.
-
ctypes.util.find_library(name) -
Попытаться найти библиотеку и вернуть путь к файлу. name — имя библиотеки без каких-либо префиксов, таких как lib, суффиксов, таких как
.so,.dylib, или номера версии (это формат, используемый для опции позиксного линковщика-l). Если библиотека не найдена, возвращаетсяNone.
Точная функциональность зависит от системы.
В Linux, find_library() пытается запустить внешние программы (/sbin/ldconfig, gcc, и objdump), чтобы найти файл библиотеки. Он возвращает имя файла библиотеки. Вот несколько примеров:
>>> 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'
>>>
В операционной системе OS X, 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() для поиска библиотеки во время выполнения.
2.2. Загрузка общих библиотек
-
class ctypes.CDLL(name, mode=DEFAULT_MODE, handle=None, use_errno=False, use_last_error=False) -
Экземпляры этого класса представляют загруженные общие библиотеки. Функции в этих библиотеках используют стандартную C-конвенцию вызова и предполагается, что они возвращают
int.
-
class ctypes.OleDLL(name, mode=DEFAULT_MODE, handle=None, use_errno=False, use_last_error=False) -
Только Windows: экземпляры этого класса представляют загруженные общие библиотеки, функции в этих библиотеках используют
stdcallконвенцию вызова и предполагается, что они возвращают специфичный для Windows кодHRESULT. ЗначенияHRESULTсодержат информацию о том, завершился ли вызов функции успешно или неудачно, а также дополнительный код ошибки. Если возвращаемое значение сигнализирует об ошибке, автоматически генерируется исключениеOSError.Изменено в версии 3.3:
WindowsErrorраньше использовался.
-
class ctypes.WinDLL(name, mode=DEFAULT_MODE, handle=None, use_errno=False, use_last_error=False) -
Только Windows: экземпляры этого класса представляют загруженные общие библиотеки, функции в этих библиотеках используют
stdcallконвенцию вызова и по умолчанию предполагается, что они возвращаютint.Только в Windows CE используется стандартная конвенция вызова; для удобства
WinDLLиOleDLLиспользуют стандартную конвенцию вызова на этой платформе.
Блокировка глобального интерпретатора Python глобального интерпретатора снимается перед вызовом любой функции, экспортированной этими библиотеками, и восстанавливается после.
-
class ctypes.PyDLL(name, mode=DEFAULT_MODE, handle=None) -
Экземпляры этого класса ведут себя как экземпляры
CDLL, за исключением того, что блокировка глобального интерпретатора Python (GIL) не снимается во время вызова функции, и после выполнения функции проверяется флаг ошибки Python. Если установлен флаг ошибки, генерируется исключение Python.Таким образом, это полезно только для вызова функций Python C API напрямую.
Все эти классы могут быть созданы, вызывая их с по меньшей мере одним аргументом — именем пути к общей библиотеке. Если у вас уже есть дескриптор уже загруженной общей библиотеки, его можно передать как параметр handle, в противном случае подлежащая платформа использует функцию dlopen или LoadLibrary для загрузки библиотеки в процесс и получения дескриптора к ней.
Параметр mode можно использовать для указания способа загрузки библиотеки. Подробности см. в справочнике dlopen(3). В Windows параметр mode игнорируется. В системах POSIX всегда добавляется RTLD_NOW, и его нельзя настроить.
Параметр use_errno, если установлен в значение true, включает механизм ctypes, который позволяет безопасно получить системное значение errno номера ошибки. ctypes сохраняет потоково-локальную копию системной переменной errno; если вы вызываете внешние функции, созданные с помощью use_errno=True, то значение errno до вызова функции меняется на копию ctypes, то же самое происходит сразу после вызова функции.
Функция ctypes.get_errno() возвращает значение копии ctypes, а функция ctypes.set_errno() изменяет копию ctypes на новое значение и возвращает предыдущее.
Параметр use_last_error, если установлен в значение true, включает тот же механизм для кода ошибки Windows, который управляется функциями GetLastError() и SetLastError() Windows API; для получения и изменения копии ctypes кода ошибки Windows используются ctypes.get_last_error() и ctypes.set_last_error().
-
ctypes.RTLD_GLOBAL -
Флаг, используемый в качестве параметра mode. На платформах, где этот флаг недоступен, он определён как целое число ноль.
-
ctypes.RTLD_LOCAL -
Флаг, используемый в качестве параметра mode. На платформах, где он недоступен, он эквивалентен RTLD_GLOBAL.
-
ctypes.DEFAULT_MODE -
Режим по умолчанию, используемый для загрузки общих библиотек. В OSX 10.3 он равен RTLD_GLOBAL, в противном случае он эквивалентен RTLD_LOCAL.
У экземпляров этих классов нет общедоступных методов. К экспортированным функциям общей библиотеки можно получить доступ как к атрибутам или по индексу. Обратите внимание, что доступ к функции через атрибут кэширует результат, поэтому повторный доступ возвращает тот же объект каждый раз. С другой стороны, доступ к нему через индекс возвращает новый объект каждый раз:
>>> libc.time == libc.time True >>> libc['time'] == libc['time'] False
Доступны следующие общедоступные атрибуты, их имена начинаются с подчеркивания, чтобы не конфликтовать с именами экспортированных функций:
-
PyDLL._handle -
Системный дескриптор, используемый для доступа к библиотеке.
-
PyDLL._name -
Имя библиотеки, переданное в конструктор.
Общие библиотеки также можно загружать, используя один из предварительно созданных объектов, которые являются экземплярами класса LibraryLoader, либо вызывая метод LoadLibrary(), либо получая библиотеку как атрибут экземпляра загрузчика.
-
class ctypes.LibraryLoader(dlltype) -
Класс, который загружает общие библиотеки. dlltype должен быть одним из типов
CDLL,PyDLL,WinDLLилиOleDLL.__getattr__()имеет специальное поведение: он позволяет загружать общую библиотеку, обращаясь к ней как к атрибуту экземпляра загрузчика библиотеки. Результат кэшируется, поэтому повторные обращения к атрибутам каждый раз возвращают ту же библиотеку.-
LoadLibrary(name) -
Загрузить общую библиотеку в процесс и вернуть её. Этот метод всегда возвращает новый экземпляр библиотеки.
-
Эти предварительно созданные загрузчики библиотек доступны:
-
ctypes.cdll -
Создаёт экземпляры
CDLL.
-
ctypes.windll -
Только Windows: Создаёт экземпляры
WinDLL.
-
ctypes.oledll -
Только Windows: Создаёт экземпляры
OleDLL.
-
ctypes.pydll -
Создаёт экземпляры
PyDLL.
Для прямого доступа к C Python API доступен готовый к использованию объект общей библиотеки Python:
-
ctypes.pythonapi -
Экземпляр
PyDLL, который предоставляет функции Python C API как атрибуты. Обратите внимание, что все эти функции предполагается возвращают Cint, что, конечно, не всегда соответствует действительности, поэтому вам необходимо назначить правильный атрибутrestypeдля использования этих функций.
2.3. Внешние функции
Как объяснялось в предыдущем разделе, к внешним функциям можно получить доступ как к атрибутам загруженных общих библиотек. Объекты функций, созданные таким образом, по умолчанию принимают любое количество аргументов, принимают любые экземпляры данных ctypes в качестве аргументов и возвращают тип результата по умолчанию, указанный загрузчиком библиотеки. Они являются экземплярами частного класса:
-
class ctypes._FuncPtr -
Базовый класс для вызовов функций на C.
Экземпляры внешних функций также являются совместимыми с C типами данных; они представляют указатели на функции C.
Это поведение можно настроить, присвоив специальные атрибуты объекту внешней функции.
-
restype -
Присвойте тип ctypes, чтобы указать тип возвращаемого значения внешней функции. Используйте
Noneдляvoid, функции, не возвращающей ничего.Можно присвоить вызываемый объект Python, который не является типом ctypes, в этом случае функция предполагается возвращающей целое число C
int, и вызываемый объект будет вызван с этим целым числом, что позволит выполнить дополнительную обработку или проверку ошибок. Использование этого устарело; для более гибкой пост-обработки или проверки ошибок используйте тип данных ctypes какrestypeи присвойте вызываемый объект атрибутуerrcheck.
-
argtypes -
Присвойте кортеж типов ctypes, чтобы указать типы аргументов, принимаемых функцией. Функции, использующие
stdcallсоглашение о вызове, могут вызываться только с таким же количеством аргументов, как длина этого кортежа; функции, использующие соглашение о вызове C, также принимают дополнительные неопределенные аргументы.При вызове внешней функции каждый фактический аргумент передаётся методу класса
from_param()элементов в кортежеargtypes; этот метод позволяет адаптировать фактический аргумент к объекту, который принимает внешняя функция. Например, элементc_char_pв кортежеargtypesпреобразует строку, переданную в качестве аргумента, в объект bytes с использованием правил преобразования ctypes.Новое: теперь можно помещать в argtypes элементы, которые не являются типами ctypes, но каждый элемент должен иметь метод
from_param(), возвращающий значение, пригодное в качестве аргумента (целое число, строка, экземпляр ctypes). Это позволяет определять адаптеры, которые могут адаптировать пользовательские объекты в качестве параметров функции.
-
errcheck -
Присвойте этому атрибуту функцию Python или другой вызываемый объект. Вызываемый объект будет вызван с тремя или более аргументами:
-
callable(result, func, arguments) -
result — это то, что возвращает внешняя функция, как указано атрибутом
restype.func — это сам объект внешней функции; это позволяет повторно использовать тот же вызываемый объект для проверки или пост-обработки результатов нескольких функций.
arguments — кортеж, содержащий параметры, изначально переданные в вызов функции; это позволяет специализировать поведение в зависимости от используемых аргументов.
Объект, возвращаемый этой функцией, будет возвращён из вызова внешней функции, но он также может проверять значение результата и генерировать исключение, если вызов внешней функции завершился ошибкой.
-
-
-
exception ctypes.ArgumentError -
Это исключение генерируется, когда внешняя функция не может преобразовать один из переданных аргументов.
2.4. Прототипы функций
Внешние функции также могут быть созданы путём создания экземпляров прототипов функций. Прототипы функций похожи на прототипы функций в C; они описывают функцию (тип возвращаемого значения, типы аргументов, соглашение о вызове) без определения реализации. Функции-фабрики должны вызываться с желаемым типом возвращаемого значения и типами аргументов функции.
-
ctypes.CFUNCTYPE(restype, *argtypes, use_errno=False, use_last_error=False) -
Возвращаемый прототип функции создаёт функции, использующие стандартное соглашение о вызове C. Функция освободит GIL во время вызова. Если use_errno установлено в true, приватная копия ctypes переменной системы
errnoменяется на реальное значениеerrnoдо и после вызова; use_last_error делает то же самое для кода ошибки Windows.
-
ctypes.WINFUNCTYPE(restype, *argtypes, use_errno=False, use_last_error=False) -
Только для Windows: возвращаемый прототип функции создаёт функции, использующие
stdcallсоглашение о вызове, за исключением Windows CE, гдеWINFUNCTYPE()совпадает сCFUNCTYPE(). Функция освободит GIL во время вызова. use_errno и use_last_error имеют то же значение, что и выше.
-
ctypes.PYFUNCTYPE(restype, *argtypes) -
Возвращаемый прототип функции создаёт функции, использующие соглашение о вызове Python. Функция не освободит GIL во время вызова.
Прототипы функций, созданные этими функциями-фабриками, могут быть созданы различными способами, в зависимости от типа и количества параметров вызова:
-
prototype(address) -
Возвращает внешнюю функцию по указанному адресу, который должен быть целым числом.
-
prototype(callable) -
Создаёт вызываемую на C функцию (функцию обратного вызова) из Python-вызываемого объекта.
-
prototype(func_spec[, paramflags]) -
Возвращает внешнюю функцию, экспортированную из динамической библиотеки. func_spec должен быть 2-кортежем
(name_or_ordinal, library). Первый элемент — имя экспортированной функции в виде строки или порядковый номер экспортированной функции в виде небольшого целого числа. Второй элемент — экземпляр динамической библиотеки.
-
prototype(vtbl_index, name[, paramflags[, iid]]) -
Возвращает внешнюю функцию, которая будет вызывать метод COM. vtbl_index — индекс в таблице виртуальных функций, небольшое неотрицательное целое число. name — имя метода COM. iid — необязательный указатель на идентификатор интерфейса, который используется в расширенных сообщениях об ошибках.
Методы COM используют специальное соглашение о вызове: они требуют указателя на COM-интерфейс в качестве первого аргумента, в дополнение к тем параметрам, которые указаны в кортеже
argtypes.
Необязательный параметр paramflags создаёт оболочки внешних функций с гораздо большими функциональными возможностями, чем описанные выше.
paramflags должен быть кортежем такой же длины, как argtypes.
Каждый элемент в этом кортеже содержит дополнительную информацию об аргументе; он должен быть кортежем, содержащим один, два или три элемента.
Первый элемент — целое число, содержащее комбинацию флагов направления для аргумента:
- 1
- Указывает входной параметр функции.
- 2
- Выходной параметр. Внешняя функция заполняет значение.
- 4
- Входной параметр, который по умолчанию равен целому числу ноль.
Необязательный второй элемент — имя параметра в виде строки. Если он указан, внешняя функция может вызываться с именованными параметрами.
Необязательный третий элемент — значение по умолчанию для этого параметра.
Этот пример демонстрирует, как обернуть функцию Windows MessageBoxW так, чтобы она поддерживала параметры по умолчанию и именованные аргументы. C-объявление из заголовочного файла Windows таково:
WINUSERAPI int WINAPI
MessageBoxW(
HWND hWnd,
LPCWSTR lpText,
LPCWSTR lpCaption,
UINT uType);
Вот обёртка с использованием ctypes:
>>> from ctypes import c_int, WINFUNCTYPE, windll
>>> from ctypes.wintypes import HWND, LPCWSTR, UINT
>>> prototype = WINFUNCTYPE(c_int, HWND, LPCWSTR, LPCWSTR, UINT)
>>> paramflags = (1, "hwnd", 0), (1, "text", "Hi"), (1, "caption", "Hello from ctypes"), (1, "flags", 0)
>>> MessageBox = prototype(("MessageBoxW", windll.user32), paramflags)
Теперь внешнюю функцию MessageBox можно вызвать следующими способами:
>>> MessageBox() >>> MessageBox(text="Spam, spam, spam") >>> MessageBox(flags=2, text="foo bar")
Второй пример демонстрирует выходные параметры. Функция win32 GetWindowRect получает размеры указанного окна, копируя их в структуру RECT , которую должен предоставить вызывающий код. Вот C-объявление:
WINUSERAPI BOOL WINAPI
GetWindowRect(
HWND hWnd,
LPRECT lpRect);
Вот обёртка с использованием ctypes:
>>> from ctypes import POINTER, WINFUNCTYPE, windll, WinError
>>> from ctypes.wintypes import BOOL, HWND, RECT
>>> prototype = WINFUNCTYPE(BOOL, HWND, POINTER(RECT))
>>> paramflags = (1, "hwnd"), (2, "lprect")
>>> GetWindowRect = prototype(("GetWindowRect", windll.user32), paramflags)
>>>
Функции с выходными параметрами автоматически возвращают значение выходного параметра, если оно единственное, или кортеж, содержащий значения выходных параметров, если их больше одного, поэтому функция GetWindowRect теперь возвращает экземпляр RECT при вызове.
Выходные параметры можно комбинировать с протоколом errcheck для дальнейшей обработки выходных данных и проверки ошибок. Функция win32 GetWindowRect возвращает BOOL для сигнализации об успехе или неудаче, поэтому эта функция может выполнять проверку ошибок и генерировать исключение, когда вызов API завершился ошибкой:
>>> def errcheck(result, func, args): ... if not result: ... raise WinError() ... return args ... >>> GetWindowRect.errcheck = errcheck >>>
Если функция errcheck возвращает кортеж аргументов без изменений, ctypes продолжает обычную обработку выходных параметров. Если вы хотите вернуть кортеж координат окна вместо экземпляра RECT , вы можете извлечь поля в функции и вернуть их вместо этого; обычная обработка больше не будет выполняться:
>>> def errcheck(result, func, args): ... if not result: ... raise WinError() ... rc = args[1] ... return rc.left, rc.top, rc.bottom, rc.right ... >>> GetWindowRect.errcheck = errcheck >>>
2.5. Вспомогательные функции
-
ctypes.addressof(obj) -
Возвращает адрес буфера памяти в виде целого числа. obj должен быть экземпляром типа ctypes.
-
ctypes.alignment(obj_or_type) -
Возвращает требования к выравниванию для типа ctypes. obj_or_type должен быть типом или экземпляром ctypes.
-
ctypes.byref(obj[, offset]) -
Возвращает лёгкий указатель на obj, который должен быть экземпляром типа ctypes. offset по умолчанию равен нулю и должен быть целым числом, которое будет добавлено к внутреннему значению указателя.
byref(obj, offset)соответствует следующему коду C:(((char *)&obj) + offset)
Возвращаемый объект может использоваться только в качестве параметра вызова внешней функции. Он ведёт себя подобно
pointer(obj), но его создание намного быстрее.
-
ctypes.cast(obj, type) -
Эта функция аналогична оператору приведения типов в C. Она возвращает новую инстанцию типа type, которая указывает на тот же блок памяти, что и obj. type должен быть типом указателя, а obj — объектом, который можно интерпретировать как указатель.
-
ctypes.create_string_buffer(init_or_size, size=None) -
Эта функция создаёт изменяемый буфер символов. Возвращаемый объект — массив ctypes типа
c_char.init_or_size должен быть целым числом, определяющим размер массива, или объектом типа bytes, который будет использован для инициализации элементов массива.
Если в качестве первого аргумента указан объект типа bytes, буфер увеличивается на один элемент больше его длины, чтобы последний элемент массива был символом завершения строки (NUL). В качестве второго аргумента можно передать целое число, позволяющее указать размер массива, если длина объекта bytes не должна использоваться.
-
ctypes.create_unicode_buffer(init_or_size, size=None) -
Эта функция создаёт изменяемый буфер символов Юникод. Возвращаемый объект — массив ctypes типа
c_wchar.init_or_size должен быть целым числом, определяющим размер массива, или строкой, которая будет использована для инициализации элементов массива.
Если в качестве первого аргумента указана строка, буфер увеличивается на один элемент больше длины строки, чтобы последний элемент массива был символом завершения строки (NUL). В качестве второго аргумента можно передать целое число, позволяющее указать размер массива, если длина строки не должна использоваться.
-
ctypes.DllCanUnloadNow() -
Только Windows: Эта функция является обработчиком, который позволяет реализовывать COM-серверы в процессе с ctypes. Она вызывается из функции DllCanUnloadNow, которую экспортирует dll расширения _ctypes.
-
ctypes.DllGetClassObject() -
Только Windows: Эта функция является обработчиком, который позволяет реализовывать COM-серверы в процессе с ctypes. Она вызывается из функции DllGetClassObject, которую экспортирует dll расширения
_ctypes.
-
ctypes.util.find_library(name) -
Попытка найти библиотеку и вернуть путь к ней. name — имя библиотеки без каких-либо префиксов, таких как
lib, суффиксов, таких как.so,.dylib, или номера версии (это формат, используемый для опции линковщика posix-l). Если библиотека не найдена, возвращаетNone.Точная функциональность зависит от системы.
-
ctypes.util.find_msvcrt() -
Только Windows: возвращает имя файла библиотеки времени выполнения VC, используемой Python и модулями расширения. Если имя библиотеки определить невозможно, возвращается
None.Если вам нужно освободить память, например, выделенную модулем расширения с помощью вызова функции
free(void *), важно, чтобы вы использовали функцию из той же библиотеки, что и выделенная память.
-
ctypes.FormatError([code]) -
Только Windows: Возвращает текстовое описание кода ошибки code. Если код ошибки не указан, используется последний код ошибки, полученный вызовом функции Windows GetLastError.
-
ctypes.GetLastError() -
Только Windows: Возвращает последний код ошибки, установленный в текущем потоке Windows. Эта функция вызывает функцию Windows
GetLastError()напрямую, она не возвращает копию кода ошибки, хранящуюся в ctypes.
-
ctypes.get_errno() -
Возвращает текущее значение копии, хранимой в ctypes, системной переменной
errnoв текущем потоке.
-
ctypes.get_last_error() -
Только Windows: возвращает текущее значение копии, хранимой в ctypes, системной переменной
LastErrorв текущем потоке.
-
ctypes.memmove(dst, src, count) -
Аналогично стандартной функции C memmove: копирует count байтов из src в dst. dst и src должны быть целыми числами или экземплярами ctypes, которые можно преобразовать в указатели.
-
ctypes.memset(dst, c, count) -
Аналогично стандартной функции C memset: заполняет блок памяти по адресу dst значением c объёмом count байтов. dst должен быть целым числом, представляющим адрес, или экземпляром ctypes.
-
ctypes.POINTER(type) -
Эта фабричная функция создаёт и возвращает новый тип указателя ctypes. Типы указателей кешируются и повторно используются внутри, поэтому повторный вызов этой функции не затратен. type должен быть типом ctypes.
-
ctypes.pointer(obj) -
Эта функция создаёт новый экземпляр указателя, указывающего на obj. Возвращаемый объект имеет тип
POINTER(type(obj)).Примечание: Если вам нужно просто передать указатель на объект в вызов внешней функции, следует использовать
byref(obj), что значительно быстрее.
-
ctypes.resize(obj, size) -
Эта функция изменяет размер внутреннего буфера памяти obj, который должен быть экземпляром типа ctypes. Невозможно уменьшить буфер меньше, чем родной размер типа объекта, как указано
sizeof(type(obj)), но можно увеличить буфер.
-
ctypes.set_errno(value) -
Устанавливает текущее значение копии, хранящейся в ctypes, системной переменной
errnoв текущем потоке на value и возвращает предыдущее значение.
-
ctypes.set_last_error(value) -
Только Windows: устанавливает текущее значение копии, хранящейся в ctypes, системной переменной
LastErrorв текущем потоке на value и возвращает предыдущее значение.
-
ctypes.sizeof(obj_or_type) -
Возвращает размер буфера памяти типа или экземпляра ctypes в байтах. Выполняет то же, что и оператор C
sizeof.
-
ctypes.string_at(address, size=-1) -
Эта функция возвращает строку C, начинающуюся по адресу памяти address, как объект типа bytes. Если указан размер size, он используется в качестве размера, в противном случае строка предполагается нуль-завершённой.
-
ctypes.WinError(code=None, descr=None) -
Только Windows: эта функция, вероятно, наименее удачно названа в ctypes. Она создаёт экземпляр OSError. Если code не указан, вызывается
GetLastError, чтобы определить код ошибки. Если descr не указан, вызываетсяFormatError(), чтобы получить текстовое описание ошибки.Изменено в версии 3.3: Создавался экземпляр
WindowsError.
-
ctypes.wstring_at(address, size=-1) -
Эта функция возвращает строку символов Юникод, начинающуюся по адресу памяти address, как строку. Если указан размер size, он используется как количество символов строки, в противном случае строка предполагается нуль-завершённой.
2.6. Типы данных
-
class ctypes._CData -
Этот непубличный класс является общим базовым классом всех типов данных ctypes. Среди прочего, все экземпляры типов ctypes содержат блок памяти, хранящий данные совместимые с C; адрес блока памяти возвращается функцией-помощником
addressof(). Другая переменная экземпляра экспортируется как_objects; она содержит другие объекты Python, которые необходимо поддерживать живыми в случае, если блок памяти содержит указатели.Общие методы типов данных ctypes, все они являются методами класса (точнее, методами метакласса):
-
from_buffer(source[, offset]) -
Этот метод возвращает экземпляр ctypes, который разделяет буфер объекта source. Объект source должен поддерживать интерфейс буфера для записи. Необязательный параметр offset указывает смещение в буфере источника в байтах; по умолчанию он равен нулю. Если буфер источника недостаточно большой, возникает исключение
ValueError.
-
from_buffer_copy(source[, offset]) -
Этот метод создаёт экземпляр ctypes, копируя буфер из буфера объекта source, который должен быть читаемым. Необязательный параметр offset указывает смещение в буфере источника в байтах; по умолчанию он равен нулю. Если буфер источника недостаточно большой, возникает исключение
ValueError.
-
from_address(address) -
Этот метод возвращает экземпляр типа ctypes, используя память, указанную адресом address, который должен быть целым числом.
-
from_param(obj) -
Этот метод адаптирует obj к типу ctypes. Он вызывается с фактическим объектом, используемым в вызове внешней функции, когда тип присутствует в кортеже
argtypesвнешней функции; он должен вернуть объект, который может быть использован как параметр вызова функции.Все типы данных ctypes имеют реализацию по умолчанию для этого метода класса, которая обычно возвращает obj, если это экземпляр типа. Некоторые типы также принимают другие объекты.
-
in_dll(library, name) -
Этот метод возвращает экземпляр типа ctypes, экспортированный общей библиотекой. name — имя символа, экспортирующего данные, library — загруженная общая библиотека.
Общие переменные экземпляров типов данных ctypes:
-
_b_base_ -
Иногда экземпляры данных ctypes не владеют блоком памяти, который они содержат, вместо этого они разделяют часть блока памяти базового объекта. Член для чтения
_b_base_— это корневой объект ctypes, который владеет блоком памяти.
-
_b_needsfree_ -
Эта переменная для чтения равна true, когда экземпляр данных ctypes сам выделил блок памяти, и false в противном случае.
-
_objects -
Этот член является либо
None, либо словарем, содержащим объекты Python, которые необходимо поддерживать живыми, чтобы содержимое блока памяти оставалось корректным. Этот объект экспортируется только для отладки; никогда не изменяйте содержимое этого словаря.
-
2.7. Основные типы данных
-
class ctypes._SimpleCData -
Этот непубличный класс является базовым классом всех основных типов данных ctypes. Он упомянут здесь, потому что содержит общие атрибуты основных типов данных ctypes.
_SimpleCDataявляется подклассом_CData, поэтому он наследует их методы и атрибуты. Типы данных ctypes, которые не являются и не содержат указателей, теперь могут быть сериализованы.У экземпляров есть один атрибут:
-
value -
Этот атрибут содержит фактическое значение экземпляра. Для целых и указательны типов — это целое число, для типов символов — это объект байтового строкового литерала или строка, для типов указателей на символы — это объект Python типа bytes или строка.
При получении атрибута
valueиз экземпляра ctypes обычно каждый раз возвращается новый объект.ctypesне реализует возврат исходного объекта, всегда создаётся новый объект. То же самое верно для всех других экземпляров объектов ctypes.
-
Основные типы данных, возвращаемые в результате вызова внешней функции или, например, при получении членов структуры или элементов массива, прозрачно преобразуются в базовые типы Python. Другими словами, если внешняя функция имеет restype типа c_char_p, вы всегда получите объект Python типа bytes, а не экземпляр c_char_p.
Подклассы основных типов данных не наследуют это поведение. Таким образом, если внешняя функция restype является подклассом c_void_p, вы получите экземпляр этого подкласса при вызове функции. Конечно, вы можете получить значение указателя, обратившись к атрибуту value.
Вот основные типы данных ctypes:
-
class ctypes.c_byte -
Представляет тип данных C
signed char, и интерпретирует значение как маленькое целое число. Конструктор принимает необязательное целочисленное начальное значение; проверка переполнения не выполняется.
-
class ctypes.c_char -
Представляет тип данных C
char, и интерпретирует значение как один символ. Конструктор принимает необязательное строковое начальное значение, длина строки должна быть ровно один символ.
-
class ctypes.c_char_p -
Представляет тип данных C
char *, когда он указывает на строку с нулевым завершением. Для общего указателя на символ, который также может указывать на двоичные данные, необходимо использоватьPOINTER(c_char). Конструктор принимает целочисленный адрес или объект типа bytes.
-
class ctypes.c_double -
Представляет тип данных C
double. Конструктор принимает необязательное начальное значение типа float.
-
class ctypes.c_longdouble -
Представляет тип данных C
long double. Конструктор принимает необязательное начальное значение типа float. На платформах, гдеsizeof(long double) == sizeof(double), он является псевдонимом дляc_double.
-
class ctypes.c_float -
Представляет тип данных C
float. Конструктор принимает необязательное начальное значение типа float.
-
class ctypes.c_int -
Представляет тип данных C
signed int. Конструктор принимает необязательное начальное значение типа integer; проверка переполнения не выполняется. На платформах, гдеsizeof(int) == sizeof(long), он является псевдонимом дляc_long.
-
class ctypes.c_int8 -
Представляет тип данных C 8-битного
signed int. Обычно является псевдонимом дляc_byte.
-
class ctypes.c_int16 -
Представляет тип данных C 16-битного
signed int. Обычно является псевдонимом дляc_short.
-
class ctypes.c_int32 -
Представляет тип данных C 32-битного
signed int. Обычно является псевдонимом дляc_int.
-
class ctypes.c_int64 -
Представляет тип данных C 64-битного
signed int. Обычно является псевдонимом дляc_longlong.
-
class ctypes.c_long -
Представляет тип данных C
signed long. Конструктор принимает необязательное целочисленное начальное значение; проверка переполнения не выполняется.
-
class ctypes.c_longlong -
Представляет тип данных C
signed long long. Конструктор принимает необязательное целочисленное начальное значение; проверка переполнения не выполняется.
-
class ctypes.c_short -
Представляет тип данных C
signed short. Конструктор принимает необязательное целочисленное начальное значение; проверка переполнения не выполняется.
-
class ctypes.c_size_t -
Представляет тип данных C
size_t.
-
class ctypes.c_ssize_t -
Представляет тип данных C
ssize_t.Добавлен в версии 3.2.
-
class ctypes.c_ubyte -
Представляет тип данных C
unsigned char, он интерпретирует значение как малое целое число. Конструктор принимает необязательное целочисленное начальное значение; проверка переполнения не выполняется.
-
class ctypes.c_uint -
Представляет C тип данных
unsigned int. Конструктор принимает необязательный целочисленный инициализатор; проверка переполнения не выполняется. На платформах, гдеsizeof(int) == sizeof(long)это псевдоним дляc_ulong.
-
class ctypes.c_uint8 -
Представляет 8-битный C тип данных
unsigned int. Обычно является псевдонимом дляc_ubyte.
-
class ctypes.c_uint16 -
Представляет 16-битный C тип данных
unsigned int. Обычно является псевдонимом дляc_ushort.
-
class ctypes.c_uint32 -
Представляет 32-битный C тип данных
unsigned int. Обычно является псевдонимом дляc_uint.
-
class ctypes.c_uint64 -
Представляет 64-битный C тип данных
unsigned int. Обычно является псевдонимом дляc_ulonglong.
-
class ctypes.c_ulong -
Представляет C тип данных
unsigned long. Конструктор принимает необязательный целочисленный инициализатор; проверка переполнения не выполняется.
-
class ctypes.c_ulonglong -
Представляет C тип данных
unsigned long long. Конструктор принимает необязательный целочисленный инициализатор; проверка переполнения не выполняется.
-
class ctypes.c_ushort -
Представляет C тип данных
unsigned short. Конструктор принимает необязательный целочисленный инициализатор; проверка переполнения не выполняется.
-
class ctypes.c_void_p -
Представляет C тип
void *. Значение представлено как целое число. Конструктор принимает необязательный целочисленный инициализатор.
-
class ctypes.c_wchar -
Представляет C тип данных
_Bool, и интерпретирует значение как строку Unicode из одного символа. Конструктор принимает необязательный строковый инициализатор, длина строки должна быть ровно один символ.
-
class ctypes.c_wchar_p -
Представляет C тип данных
wchar_t *, который должен быть указателем на нуль-терминированную строку широких символов. Конструктор принимает целочисленный адрес или строку.
-
class ctypes.c_bool -
Представляет C тип данных
bool(точнее_Boolиз C99). Его значение может бытьTrueилиFalse, и конструктор принимает любой объект, имеющий логическое значение.
-
class ctypes.HRESULT -
Только Windows: Представляет значение
HRESULT, которое содержит информацию об успехе или ошибке для вызова функции или метода.
-
class ctypes.py_object -
Представляет C тип
PyObject *. Вызов без аргументов создает указатель наNULLPyObject *.
Модуль ctypes.wintypes предоставляет некоторые другие типы данных, специфичные для Windows, например, HWND, WPARAM или DWORD. Также определены некоторые полезные структуры, такие как MSG или RECT.
2.8. Структурированные типы данных
-
class ctypes.Union(*args, **kw) -
Абстрактный базовый класс для объединений в родном байтовом порядке.
-
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_после оператора класса, который определяет подкласс Structure, это позволяет создавать типы данных, которые напрямую или косвенно ссылаются на себя:class List(Structure): pass List._fields_ = [("pnext", POINTER(List)), ... ]Однако, переменная класса
_fields_должна быть определена перед первым использованием типа (создаётся экземпляр, вызываетсяsizeof()и т.д.). Позднее присвоения переменной класса_fields_приведут к ошибке AttributeError.Можно определять подклассы структур, они наследуют поля базового класса плюс поля, определённые в подклассе, если таковые имеются.
-
_pack_ -
Необязательное целое число, которое позволяет переопределить выравнивание полей структуры в экземпляре.
_pack_должно быть уже определено, когда присваивается_fields_, в противном случае оно не будет иметь эффекта.
-
_anonymous_ -
Необязательная последовательность, содержащая имена безымянных (анонимных) полей.
_anonymous_должно быть уже определено, когда присваивается_fields_, в противном случае это не повлияет.Поля, перечисленные в этой переменной, должны быть типами структур или объединений.
ctypesсоздаст дескрипторы в типе структуры, позволяющие обращаться к вложенным полям напрямую без необходимости создания экземпляра структуры или объединения.Вот пример типа (Windows):
class _U(Union): _fields_ = [("lptdesc", POINTER(TYPEDESC)), ("lpadesc", POINTER(ARRAYDESC)), ("hreftype", HREFTYPE)] class TYPEDESC(Structure): _anonymous_ = ("u",) _fields_ = [("u", _U), ("vt", VARTYPE)]Структура
TYPEDESCописывает тип данных COM, полеvtуказывает, какое из полей объединения допустимо. Так как полеuопределено как анонимное поле, теперь можно напрямую обращаться к членам экземпляра TYPEDESC.td.lptdescиtd.u.lptdescэквивалентны, но первое быстрее, так как не нужно создавать временный экземпляр объединения:td = TYPEDESC() td.vt = VT_PTR td.lptdesc = POINTER(some_type) td.u.lptdesc = POINTER(some_type)
Можно определять подклассы структур, они наследуют поля базового класса. Если в определении подкласса есть отдельная переменная
_fields_, поля, указанные в ней, добавляются к полям базового класса.Конструкторы структур и объединений принимают позиционные и ключевые аргументы. Позиционные аргументы используются для инициализации полей членов в том же порядке, в котором они появляются в
_fields_. Ключевые аргументы в конструкторе интерпретируются как присвоения атрибутов, поэтому они будут инициализировать_fields_с тем же именем или создавать новые атрибуты для имен, отсутствующих в_fields_. -
2.9. Массивы и указатели
-
class ctypes.Array(*args) -
Абстрактный базовый класс для массивов.
Рекомендуемый способ создания конкретных типов массивов — умножение любого типа данных
ctypesна положительное целое число. В качестве альтернативы вы можете создать подкласс этого типа и определить переменные класса_length_и_type_. Элементы массива можно читать и записывать с помощью стандартных индексных и срезовых операций; при чтении срезов результирующий объект не является объектом типаArray.-
_length_ -
Положительное целое число, определяющее количество элементов в массиве. Индексы вне допустимого диапазона приводят к исключению
IndexError. Возвращается функциейlen().
-
_type_ -
Определяет тип каждого элемента в массиве.
Конструкторы подклассов массивов принимают позиционные аргументы, используемые для инициализации элементов в порядке.
-
-
class ctypes._Pointer -
Закрытый, абстрактный базовый класс для указателей.
Конкретные типы указателей создаются путем вызова
POINTER()с типом, на который будет указывать указатель; это делается автоматически функциейpointer().Если указатель указывает на массив, его элементы можно читать и записывать с помощью стандартных индексных и срезовых операций. Объекты указателей не имеют размера, поэтому функция
len()вызовет исключениеTypeError. Отрицательные индексы будут читать данные из памяти перед указателем (как в C), а индексы вне диапазона, вероятно, приведут к ошибке доступа (если вам повезет).-
_type_ -
Определяет тип, на который указывает указатель.
-
contents -
Возвращает объект, на который указывает указатель. Присвоение значению этого атрибута изменяет указатель так, чтобы он указывал на присвоенный объект.
-
© 2001–2020 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.5/library/ctypes.html