ctypes — Библиотека внешних функций для Python
ctypes является библиотекой внешних функций для Python. Она предоставляет совместимые с C типы данных и позволяет вызывать функции в DLL или динамических библиотеках. Она может быть использована для обертывания этих библиотек чистым Python.
16.16.1. Учебник по ctypes
Примечание: Примеры кода в этом учебнике используют doctest, чтобы убедиться, что они действительно работают. Поскольку некоторые примеры кода ведут себя по-разному в Linux, Windows или Mac OS X, они содержат директивы doctest в комментариях.
Примечание: Некоторые примеры кода ссылаются на тип ctypes c_int. На платформах, где sizeof(long) == sizeof(int), он является псевдонимом для c_long. Поэтому вы не должны удивляться, если будет выводиться c_long, если вы ожидаете c_int — на самом деле это один и тот же тип.
16.16.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 ...>
>>>
16.16.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, а затем вызвать её с объектами 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
>>>
16.16.1.3. Вызов функций
Эти функции можно вызывать как любые другие вызываемые объекты Python. В этом примере используется функция time(), которая возвращает системное время в секундах с начала эпохи Unix, и функция GetModuleHandleA(), которая возвращает дескриптор модуля win32.
В этом примере обе функции вызываются с указателем NULL (None должен использоваться как указатель NULL):
>>> print(libc.time(None)) 1150640792 >>> print(hex(windll.kernel32.GetModuleHandleA(None))) 0x1d000000 >>>
Примечание
ctypes может вызывать ValueError после вызова функции, если обнаружит, что передано неверное количество аргументов. На это поведение не стоит полагаться. Оно устарело в 3.6.2 и будет удалено в 3.7.
ValueError возникает, когда вызывается функция stdcall с cdecl конвенцией вызова, или наоборот:
>>> cdll.kernel32.GetModuleHandleA(None) Traceback (most recent call last): File "<stdin>", line 1, in <module> ValueError: Procedure probably called with not enough arguments (4 bytes missing) >>> >>> windll.msvcrt.printf(b"spam") Traceback (most recent call last): File "<stdin>", line 1, in <module> ValueError: Procedure probably called with too many arguments (4 bytes in excess) >>>
Чтобы определить правильную конвенцию вызова, необходимо обратиться к файлу заголовков C или документации для вызываемой функции.
В Windows, ctypes использует обработку структурированных исключений win32 для предотвращения сбоев из-за ошибок защиты при вызове функций с недопустимыми значениями аргументов:
>>> windll.kernel32.GetModuleHandleA(32) Traceback (most recent call last): File "<stdin>", line 1, in <module> OSError: exception: access violation reading 0x00000020 >>>
Тем не менее, существуют способы аварийного завершения работы Python с использованием ctypes, поэтому необходимо проявлять осторожность. Модуль faulthandler может быть полезен для отладки сбоев (например, от сбоев, вызванных ошибочными вызовами библиотек C).
None, целые числа, объекты bytes и (строки unicode) — единственные встроенные объекты Python, которые могут напрямую использоваться в качестве параметров в этих вызовах функций. None передается как указатель C NULL, объекты bytes и строки передаются как указатели на блок памяти, содержащий их данные (char * или wchar_t *). Целые числа Python передаются как стандартный тип C платформы int, их значения маскируются, чтобы соответствовать типу C.
Прежде чем перейти к вызову функций с другими типами параметров, необходимо узнать больше о типах данных ctypes.
16.16.1.4. Основные типы данных
ctypes определяет ряд примитивных типов данных, совместимых с C:
Тип ctypes | Тип C | Тип Python |
|---|---|---|
| bool (1) | |
| объект bytes длиной в 1 символ | |
| строка длиной в 1 символ | |
| int | |
| int | |
| int | |
| int | |
| int | |
| int | |
| int | |
| int | |
| int | |
| int | |
| int | |
| int | |
| float | |
| float | |
| float | |
| объект bytes или | |
| строка или | |
| int или |
- Конструктор принимает любой объект с истинным значением.
Все эти типы можно создать, вызвав их с необязательным инициализатором правильного типа и значения:
>>> 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; если вы хотите получить доступ к нему как к строке, завершаемой нулём, используйте свойство value:
>>> from ctypes import * >>> p = create_string_buffer(3) # create a 3 byte buffer, initialized to NUL bytes >>> print(sizeof(p), repr(p.raw)) 3 b'\x00\x00\x00' >>> p = create_string_buffer(b"Hello") # create a buffer containing a NUL terminated string >>> print(sizeof(p), repr(p.raw)) 6 b'Hello\x00' >>> print(repr(p.value)) b'Hello' >>> p = create_string_buffer(b"Hello", 10) # create a 10 byte buffer >>> print(sizeof(p), repr(p.raw)) 10 b'Hello\x00\x00\x00\x00\x00' >>> p.value = b"Hi" >>> print(sizeof(p), repr(p.raw)) 10 b'Hi\x00lo\x00\x00\x00\x00\x00' >>>
Функция create_string_buffer() заменяет функцию c_buffer() (которая всё ещё доступна как псевдоним), а также функцию c_string() из более ранних выпусков ctypes. Для создания изменяемого блока памяти, содержащего символы Юникода типа C wchar_t, используйте функцию create_unicode_buffer().
16.16.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 >>>
16.16.1.6. Вызов функций с собственными пользовательскими типами данных
Вы также можете настроить преобразование аргументов ctypes, чтобы разрешить использование экземпляров ваших собственных классов в качестве аргументов функций. ctypes ищет атрибут _as_parameter_ и использует его в качестве аргумента функции. Конечно, это должно быть целое число, строка или байты:
>>> class Bottles: ... def __init__(self, number): ... self._as_parameter_ = number ... >>> bottles = Bottles(42) >>> printf(b"%d bottles of beer\n", bottles) 42 bottles of beer 19 >>>
Если вы не хотите хранить данные экземпляра в переменной экземпляра _as_parameter_, вы можете определить свойство property, которое делает атрибут доступным по запросу.
16.16.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 в этом случае. Опять же, результат должен быть целым числом, строкой, байтами, экземпляром ctypes или объектом с атрибутом _as_parameter_.
16.16.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, содержащего один символ, в символ 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; см. справочное руководство для получения подробностей.
16.16.1.9. Передача указателей (или: передача параметров по ссылке)
Иногда функция 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' >>>
16.16.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, это не гарантируется библиотекой в общем случае. Объединения и структуры с битами полей всегда должны передаваться в функции по указателю.
16.16.1.11. Выравнивание и порядок байтов структур/объединений
По умолчанию поля структур и объединений выравниваются так же, как это делает компилятор C. Можно переопределить это поведение, указав атрибут класса _pack_ в определении подкласса. Он должен быть установлен на положительное целое число и определяет максимальное выравнивание для полей. Именно это делает #pragma pack(n) в MSVC.
ctypes использует родной порядок байтов для структур и объединений. Для построения структур с неродным порядком байтов можно использовать один из базовых классов BigEndianStructure, LittleEndianStructure, BigEndianUnion и LittleEndianUnion. Эти классы не могут содержать поля указателей.
16.16.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>
>>>
16.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 >>>
16.16.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
>>>
16.16.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 >>>
16.16.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 >>>
16.16.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_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-потока.
16.16.1.18. Доступ к значениям, экспортированным из DLL
Некоторые библиотеки динамической компоновки не только экспортируют функции, но и экспортируют переменные. Примером в самой библиотеке Python является Py_OptimizeFlag, целое число, устанавливаемое в 0, 1 или 2 в зависимости от флага -O или -OO, заданного при запуске.
ctypes может получить доступ к таким значениям с помощью методов класса in_dll() типа. pythonapi — это предопределённая переменная, обеспечивающая доступ к C-API Python:
>>> 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__.
16.16.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-объект каждый раз!
16.16.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 и (пере)определении типа данных после того, как требуемый размер уже известен, в каждом отдельном случае.
16.16.2. Справочник по ctypes
16.16.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 и ld) для поиска файла библиотеки. Возвращает имя файла библиотеки.
Изменено в версии 3.6: В Linux, при поиске библиотек используется значение переменной среды LD_LIBRARY_PATH, если библиотека не может быть найдена другими способами.
Вот несколько примеров:
>>> from ctypes.util import find_library
>>> find_library("m")
'libm.so.6'
>>> find_library("c")
'libc.so.6'
>>> find_library("bz2")
'libbz2.so.1.0'
>>>
В 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() для поиска библиотеки во время выполнения.
16.16.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 (GIL) освобождается перед вызовом любой функции, экспортированной этими библиотеками, и приобретается после.
-
class ctypes.PyDLL(name, mode=DEFAULT_MODE, handle=None) -
Экземпляры этого класса ведут себя как экземпляры
CDLL, за исключением того, что 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, который управляется функциями API Windows GetLastError() и SetLastError(); используются функции ctypes.get_last_error() и ctypes.set_last_error() для получения и изменения частной копии ctypes кода ошибки Windows.
-
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.
Для прямого доступа к API C Python доступен готовый объект Python-совместимой общей библиотеки:
-
ctypes.pythonapi -
Экземпляр
PyDLL, который предоставляет функции Python C API как атрибуты. Обратите внимание, что предполагается, что все эти функции возвращают Cint, что, конечно, не всегда соответствует действительности, поэтому вам необходимо назначить правильный атрибутrestypeдля использования этих функций.
16.16.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 -
Это исключение возникает, когда внешняя функция не может преобразовать один из переданных аргументов.
16.16.2.4. Прототипы функций
Внешние функции также могут быть созданы путём создания экземпляров прототипов функций. Прототипы функций аналогичны прототипам функций в C; они описывают функцию (тип возвращаемого значения, типы аргументов, соглашение о вызове), не определяя реализацию. Функции-фабрики должны вызываться с желаемым типом возвращаемого значения и типами аргументов функции и могут использоваться в качестве фабрик декораторов, и таким образом применяться к функциям с помощью синтаксиса @wrapper. См. Функции обратного вызова для примеров.
-
ctypes.CFUNCTYPE(restype, *argtypes, use_errno=False, use_last_error=False) -
Возвращаемый прототип функции создаёт функции, использующие стандартное соглашение о вызове C. Функция освободит GIL во время вызова. Если use_errno установлено в true, частная копия ctypes переменной системы
errnoменяется на реальное значениеerrnoдо и после вызова; use_last_error делает то же самое для кода ошибки Windows.
-
ctypes.WINFUNCTYPE(restype, *argtypes, use_errno=False, use_last_error=False) -
Только Windows: Возвращаемый прототип функции создаёт функции, использующие соглашение о вызове
stdcall, за исключением 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 API возвращает значение 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 >>>
16.16.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, он используется как количество символов строки, в противном случае строка предполагается завершаемой нулём.
16.16.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, которые необходимо поддерживать живыми, чтобы содержимое блока памяти оставалось действительным. Этот объект показан только для отладки; никогда не изменяйте содержимое этого словаря.
-
16.16.2.7. Основные типы данных
-
class ctypes._SimpleCData -
Этот закрытый для пользователя класс является базовым классом всех основных типов данных ctypes. Он упоминается здесь, потому что содержит общие атрибуты основных типов данных ctypes.
_SimpleCDataявляется подклассом_CData, поэтому он наследует их методы и атрибуты. Типы данных ctypes, которые не являются и не содержат указателей, теперь могут быть сериализованы.Экземпляры имеют один атрибут:
-
value -
Этот атрибут содержит фактическое значение экземпляра. Для целочисленных и указательных типов это целое число, для символьных типов это одиночный объект байтового типа или строка, для типов указателей символов это объект Python байтового типа или строка.
Когда атрибут
valueизвлекается из экземпляра ctypes, обычно каждый раз возвращается новый объект.ctypesне реализует возвращение исходного объекта, всегда строится новый объект. То же самое верно для всех других экземпляров объектов ctypes.
-
Основные типы данных при возвращении в качестве результатов вызова внешних функций или, например, при извлечении членов структуры или элементов массива, прозрачно преобразуются в родные типы Python. Другими словами, если внешняя функция имеет тип restype типа c_char_p, вы всегда получите объект Python байтового типа, а не экземпляр c_char_p.
Подклассы основных типов данных не наследуют это поведение. Таким образом, если внешняя функция restype является подклассом c_void_p, вы получите экземпляр этого подкласса из вызова функции. Конечно, вы можете получить значение указателя, обратившись к атрибуту value.
Вот основные типы данных ctypes:
-
class ctypes.c_byte -
Представляет тип данных C
signed charи интерпретирует значение как малое целое число. Конструктор принимает необязательный целочисленный инициализатор; проверка переполнения не выполняется.
-
class ctypes.c_char -
Представляет тип данных C
charи интерпретирует значение как один символ. Конструктор принимает необязательный строковый инициализатор, длина строки должна быть ровно один символ.
-
class ctypes.c_char_p -
Представляет тип данных C
char *, когда он указывает на строку с нулевым окончанием. Для общего указателя символов, который также может указывать на двоичные данные, нужно использоватьPOINTER(c_char). Конструктор принимает целочисленный адрес или объект байтового типа.
-
class ctypes.c_double -
Представляет тип данных C
double. Конструктор принимает необязательный инициализатор с плавающей точкой.
-
class ctypes.c_longdouble -
Представляет тип данных C
long double. Конструктор принимает необязательный инициализатор с плавающей точкой. На платформах, гдеsizeof(long double) == sizeof(double), это псевдоним дляc_double.
-
class ctypes.c_float -
Представляет тип данных C
float. Конструктор принимает необязательный инициализатор с плавающей точкой.
-
class ctypes.c_int -
Представляет тип данных C
signed int. Конструктор принимает необязательный целочисленный инициализатор; проверка переполнения не выполняется. На платформах, гдеsizeof(int) == sizeof(long), это псевдоним дляc_long.
-
class ctypes.c_int8 -
Представляет 8-битный тип данных C
signed int. Обычно является псевдонимом дляc_byte.
-
class ctypes.c_int16 -
Представляет 16-битный тип данных C
signed int. Обычно является псевдонимом дляc_short.
-
class ctypes.c_int32 -
Представляет 32-битный тип данных C
signed int. Обычно является псевдонимом дляc_int.
-
class ctypes.c_int64 -
Представляет 64-битный тип данных C
signed int. Обычно является псевдонимом дляc_longlong.
-
class ctypes.c_long -
Представляет тип данных C
signed long. Конструктор принимает необязательный целочисленный инициализатор; проверка переполнения не выполняется.
-
class ctypes.c_longlong -
Представляет тип данных C
signed long long. Конструктор принимает необязательный целочисленный инициализатор; проверка переполнения не выполняется.
-
class ctypes.c_short -
Представляет тип данных C
signed short. Конструктор принимает необязательный целочисленный инициализатор; проверка переполнения не выполняется.
-
class ctypes.c_size_t -
Представляет тип данных C
size_t.
-
class ctypes.c_ssize_t -
Представляет тип данных C
ssize_t.Введено в версии 3.2.
-
class ctypes.c_ubyte -
Представляет тип данных C
unsigned char, он интерпретирует значение как малое целое число. Конструктор принимает необязательный целочисленный инициализатор; проверка переполнения не выполняется.
-
class ctypes.c_uint -
Представляет тип данных C
unsigned int. Конструктор принимает необязательный целочисленный инициализатор; проверка переполнения не выполняется. На платформах, гдеsizeof(int) == sizeof(long), это псевдоним дляc_ulong.
-
class ctypes.c_uint8 -
Представляет 8-битный тип данных C
unsigned int. Обычно является псевдонимом дляc_ubyte.
-
class ctypes.c_uint16 -
Представляет 16-битный тип данных C
unsigned int. Обычно является псевдонимом дляc_ushort.
-
class ctypes.c_uint32 -
Представляет 32-битный тип данных C
unsigned int. Обычно является псевдонимом дляc_uint.
-
class ctypes.c_uint64 -
Представляет 64-битный тип данных C
unsigned int. Обычно является псевдонимом дляc_ulonglong.
-
class ctypes.c_ulong -
Представляет тип данных C
unsigned long. Конструктор принимает необязательный целочисленный инициализатор; проверка переполнения не выполняется.
-
class ctypes.c_ulonglong -
Представляет тип данных C
unsigned long long. Конструктор принимает необязательный целочисленный инициализатор; проверка переполнения не выполняется.
-
class ctypes.c_ushort -
Представляет тип данных C
unsigned short. Конструктор принимает необязательный целочисленный инициализатор; проверка переполнения не выполняется.
-
class ctypes.c_void_p -
Представляет тип C
void *. Значение представлено в виде целого числа. Конструктор принимает необязательный целочисленный инициализатор.
-
class ctypes.c_wchar -
Представляет тип данных C
wchar_tи интерпретирует значение как строку Unicode с одним символом. Конструктор принимает необязательный строковый инициализатор; длина строки должна быть ровно один символ.
-
class ctypes.c_wchar_p -
Представляет тип данных C
wchar_t *, который должен быть указателем на строку широких символов с нулевым завершением. Конструктор принимает целочисленный адрес или строку.
-
class ctypes.c_bool -
Представляет тип данных C
bool(точнее,_Boolиз C99). Его значение может бытьTrueилиFalse, а конструктор принимает любой объект, имеющий истинное значение.
-
class ctypes.HRESULT -
Только Windows: Представляет значение
HRESULT, которое содержит информацию об успехе или ошибке для вызова функции или метода.
-
class ctypes.py_object -
Представляет тип данных C
PyObject *. Вызов без аргументов создаёт указатель наNULLPyObject *.
Модуль ctypes.wintypes предоставляет ещё ряд других типов данных, специфичных для Windows, например, HWND, WPARAM или DWORD. Также определены некоторые полезные структуры, такие как MSG или RECT.
16.16.2.8. Структурированные типы данных
-
class ctypes.Union(*args, **kw) -
Абстрактный базовый класс для объединений в родном порядке байтов.
-
class ctypes.BigEndianStructure(*args, **kw) -
Абстрактный базовый класс для структур в большом конце порядке байтов.
-
class ctypes.LittleEndianStructure(*args, **kw) -
Абстрактный базовый класс для структур в малом конце порядке байтов.
Структуры с неродным порядком байтов не могут содержать поля типа указателя или любые другие типы данных, содержащие поля типа указателя.
-
class ctypes.Structure(*args, **kw) -
Абстрактный базовый класс для структур в родном порядке байтов.
Конкретные типы структур и объединений должны создаваться путём наследования от одного из этих типов и, по крайней мере, определения переменной класса
_fields_.ctypesсоздаст дескрипторы, которые позволяют читать и записывать поля путём прямого доступа к атрибутам. Это-
_fields_ -
Последовательность, определяющая поля структуры. Элементы должны быть 2-кортежами или 3-кортежами. Первый элемент — имя поля, второй элемент — тип поля; это может быть любой тип данных ctypes.
Для полей целочисленного типа, таких как
c_int, можно указать необязательный третий элемент. Он должен быть небольшим положительным целым числом, определяющим разрядность поля.Имена полей должны быть уникальными в пределах одной структуры или объединения. Это не проверяется, только одно поле может быть обращено при совпадении имён.
Возможна декларация переменной класса
_fields_*после* объявления класса, определяющего подкласс Structure, что позволяет создавать типы данных, которые непосредственно или косвенно ссылаются на себя:class List(Structure): pass List._fields_ = [("pnext", POINTER(List)), ... ]Однако переменная класса
_fields_должна быть определена до первого использования типа (создание экземпляра, вызовsizeof()и т.д.). Позднее присвоение значения переменной класса_fields_вызовет AttributeError.Возможны подклассы структур, они наследуют поля базового класса плюс
_fields_определённые в подклассе, если таковые имеются.
-
_pack_ -
Необязательное целое число, которое позволяет переопределить выравнивание полей структуры в экземпляре.
_pack_должно быть определено до присвоения_fields_, в противном случае это не повлияет.
-
_anonymous_ -
Необязательная последовательность, которая перечисляет имена безымянных (анонимных) полей.
_anonymous_должно быть определено до присвоения_fields_, в противном случае это не повлияет.Поля, указанные в этой переменной, должны быть типами структуры или объединения.
ctypesсоздаст дескрипторы в типе структуры, которые позволяют обращаться к вложенным полям непосредственно без необходимости создавать экземпляр структуры или объединения.Вот пример типа (Windows):
class _U(Union): _fields_ = [("lptdesc", POINTER(TYPEDESC)), ("lpadesc", POINTER(ARRAYDESC)), ("hreftype", HREFTYPE)] class TYPEDESC(Structure): _anonymous_ = ("u",) _fields_ = [("u", _U), ("vt", VARTYPE)]Структура
TYPEDESCописывает тип данных COM, полеvtуказывает, какое из полей объединения является действительным. Так как полеuопределено как анонимное поле, теперь можно обратиться к членам непосредственно из экземпляра TYPEDESC.td.lptdescиtd.u.lptdescэквивалентны, но первый вариант быстрее, поскольку ему не нужно создавать временный экземпляр объединения:td = TYPEDESC() td.vt = VT_PTR td.lptdesc = POINTER(some_type) td.u.lptdesc = POINTER(some_type)
Возможны подклассы структур, они наследуют поля базового класса. Если в определении подкласса есть отдельная переменная
_fields_, поля, указанные в ней, добавляются к полям базового класса.Конструкторы структур и объединений принимают позиционные и именованные аргументы. Позиционные аргументы используются для инициализации полей членов в том же порядке, в котором они появляются в
_fields_. Именованные аргументы в конструкторе интерпретируются как присваивание атрибутов, поэтому они будут инициализировать_fields_с тем же именем или создавать новые атрибуты для имён, отсутствующих в_fields_. -
16.16.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.6/library/ctypes.html