ctypes — Библиотека внешних функций для Python
Новая в версии 2.5.
ctypes — это библиотека внешних функций для Python. Она предоставляет совместимые с C типы данных и позволяет вызывать функции из DLL или динамических библиотек. Она может быть использована для обертывания этих библиотек в чистом Python.
1. Руководство по ctypes
Примечание: Примеры кода в этом руководстве используют doctest, чтобы убедиться, что они работают. Поскольку некоторые примеры кода ведут себя по-разному под Linux, Windows или Mac OS X, они содержат директивы doctest в комментариях.
Примечание: Некоторые примеры кода ссылаются на тип ctypes c_int. Этот тип является псевдонимом для типа c_long на 32-битных системах. Поэтому вы не должны путаться, если будет напечатан c_long, когда вы ожидаете c_int — на самом деле они являются одним и тем же типом.
1.1. Загрузка динамических библиотек
ctypes экспортирует объекты cdll, а на Windows — windll и oledll для загрузки динамических библиотек.
Библиотеки загружаются путем доступа к ним как к атрибутам этих объектов. cdll загружает библиотеки, которые экспортируют функции, используя стандартную cdecl конвенцию вызова, в то время как windll загружает библиотеки, вызывающие функции с использованием stdcall конвенции вызова. oledll также использует stdcall конвенцию вызова и предполагает, что функции возвращают код ошибки Windows HRESULT. Код ошибки используется для автоматического повышения исключения WindowsError при неудачном вызове функции.
Вот несколько примеров для 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 расширение файла.
В 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("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> WindowsError: exception: access violation reading 0x00000020 >>>
Однако, существуют достаточно способы аварийного завершения Python с помощью ctypes, поэтому следует проявлять осторожность.
Целые числа, длинные целые числа, байтовые строки и строки Юникода — единственные встроенные объекты Python, которые могут напрямую использоваться в качестве параметров в этих вызовах функций. None передаётся как указатель C None, байтовые строки и строки Юникода передаются как указатель на блок памяти, содержащий их данные (NULL или char *). Целые числа Python и длинные целые числа Python передаются как типовой C-тип платформы wchar_t *, их значение маскируется для соответствия типу C.
Прежде чем перейти к вызову функций с другими типами параметров, мы должны узнать больше о типах данных int.
1.4. Основные типы данных
ctypes определяет ряд примитивных типов данных, совместимых с C:
Тип ctypes | Тип C | Тип Python |
|---|---|---|
| bool (1) | |
| строка из 1 символа | |
| строка Юникода из 1 символа | |
| int/long | |
| ... (остальные строки таблицы) | ... | ... |
- Конструктор принимает любой объект с истинным значением.
Все эти типы можно создать, вызвав их с необязательным инициализатором соответствующего типа и значения:
>>> c_int()
c_long(0)
>>> c_char_p("Hello, World")
c_char_p('Hello, World')
>>> 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 неизменяемы):
>>> s = "Hello, World"
>>> c_s = c_char_p(s)
>>> print c_s
c_char_p('Hello, World')
>>> c_s.value = "Hi, there"
>>> print c_s
c_char_p('Hi, there')
>>> print s # first string 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 '\x00\x00\x00'
>>> p = create_string_buffer("Hello") # create a buffer containing a NUL terminated string
>>> print sizeof(p), repr(p.raw)
6 'Hello\x00'
>>> print repr(p.value)
'Hello'
>>> p = create_string_buffer("Hello", 10) # create a 10 byte buffer
>>> print sizeof(p), repr(p.raw)
10 'Hello\x00\x00\x00\x00\x00'
>>> p.value = "Hi"
>>> print sizeof(p), repr(p.raw)
10 'Hi\x00lo\x00\x00\x00\x00\x00'
>>>
Функция create_string_buffer() заменяет функцию c_buffer() (которая всё ещё доступна как псевдоним), а также функцию c_string() из предыдущих версий ctypes. Для создания изменяемого блока памяти, содержащего символы Юникода типа C wchar_t, используйте функцию create_unicode_buffer().
1.5. Вызов функций, продолжение
Обратите внимание, что printf выводит данные в реальный канал стандартного вывода, а не в sys.stdout, поэтому эти примеры будут работать только в командной строке, а не внутри IDLE или PythonWin:
>>> printf = libc.printf
>>> printf("Hello, %s\n", "World!")
Hello, World!
14
>>> printf("Hello, %S\n", u"World!")
Hello, World!
14
>>> printf("%d bottles of beer\n", 42)
42 bottles of beer
19
>>> printf("%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, кроме целых чисел, строк и строк Юникода, должны быть обернуты в соответствующий тип ctypes, чтобы их можно было преобразовать в требуемый тип данных C:
>>> printf("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_ и использует его как аргумент функции. Конечно, это должно быть целое число, строка или строка Юникода:
>>> class Bottles(object):
... def __init__(self, number):
... self._as_parameter_ = number
...
>>> bottles = Bottles(42)
>>> printf("%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("String '%s', Int %d, Double %f\n", "Hi", 10, 2.2)
String 'Hi', Int 10, Double 2.200000
37
>>>
Указание формата защищает от несовместимых типов аргументов (точно так же, как прототип функции C) и пытается преобразовать аргументы в допустимые типы:
>>> printf("%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("%s %d %f\n", "X", 2, 3)
X 2 3.000000
13
>>>
Если вы определили собственные классы, которые передаёте в вызовы функций, вам нужно реализовать метод класса from_param() для их использования в последовательности argtypes. Метод класса from_param() получает объект Python, переданный в вызов функции; он должен выполнить проверку типа или любые другие необходимые действия, чтобы убедиться, что этот объект приемлем, и затем вернуть сам объект, его атрибут _as_parameter_ или то, что вы хотите передать в качестве аргумента функции C в этом случае. Результат должен быть целым числом, строкой, строкой Юникода, экземпляром ctypes или объектом с атрибутом _as_parameter_.
1.8. Типы возвращаемых значений
По умолчанию предполагается, что функции возвращают тип C int. Другие типы возвращаемых значений можно указать, установив атрибут restype объекта функции.
Вот более продвинутый пример, он использует функцию strchr, которая ожидает указатель на строку и символ и возвращает указатель на строку:
>>> strchr = libc.strchr
>>> strchr("abcdef", ord("d"))
8059983
>>> strchr.restype = c_char_p # c_char_p is a pointer to a string
>>> strchr("abcdef", ord("d"))
'def'
>>> print strchr("abcdef", ord("x"))
None
>>>
Если вы хотите избежать вызовов ord("x") выше, вы можете установить атрибут argtypes, и второй аргумент будет преобразован из строки Python в одиночный символ C:
>>> strchr.restype = c_char_p
>>> strchr.argtypes = [c_char_p, c_char]
>>> strchr("abcdef", "d")
'def'
>>> strchr("abcdef", "def")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ArgumentError: argument 2: exceptions.TypeError: one character string expected
>>> print strchr("abcdef", "x")
None
>>> strchr("abcdef", "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
WindowsError: [Errno 126] The specified module could not be found.
>>>
WinError — функция, которая вызывает Windows API FormatMessage() для получения строкового представления кода ошибки и возвращает исключение. WinError принимает необязательный параметр кода ошибки; если он не указан, вызывается GetLastError() для его извлечения.
Обратите внимание, что гораздо более мощный механизм проверки ошибок доступен через атрибут errcheck; см. справочник для получения подробностей.
1.9. Передача указателей (или: передача параметров по ссылке)
Иногда функция API C ожидает указатель на тип данных в качестве параметра, вероятно, для записи в соответствующее местоположение, или если данные слишком велики для передачи по значению. Это также известно как передача параметров по ссылке.
ctypes экспортирует функцию byref(), которая используется для передачи параметров по ссылке. Тот же эффект можно получить с помощью функции pointer(), хотя pointer() выполняет больше работы, так как создаёт реальный объект указателя, поэтому для случаев, когда вам не нужен сам объект указателя в Python, использование byref() предпочтительнее:
>>> i = c_int()
>>> f = c_float()
>>> s = create_string_buffer('\000' * 32)
>>> print i.value, f.value, repr(s.value)
0 0.0 ''
>>> libc.sscanf("1 3.14 Hello", "%d %f %s",
... byref(i), byref(f), s)
3
>>> print i.value, f.value, repr(s.value)
1 3.1400001049 '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>
TypeError: too many initializers
>>>
Однако вы можете создавать гораздо более сложные структуры. Структура может содержать другие структуры, используя структуру в качестве типа поля.
Вот структура RECT, которая содержит две структуры POINT с именами upperleft и lowerright:
>>> class RECT(Structure):
... _fields_ = [("upperleft", POINT),
... ("lowerright", POINT)]
...
>>> rc = RECT(point)
>>> print rc.upperleft.x, rc.upperleft.y
0 5
>>> print rc.lowerright.x, rc.lowerright.y
0 0
>>>
Вложенные структуры также можно инициализировать в конструкторе несколькими способами:
>>> r = RECT(POINT(1, 2), POINT(3, 4)) >>> r = RECT((1, 2), (3, 4))
Описание полей можно получить из класса; они полезны для отладки, потому что могут предоставить полезную информацию:
>>> print POINT.x <Field type=c_long, ofs=0, size=4> >>> print POINT.y <Field type=c_long, ofs=4, size=4> >>>
Предупреждение
ctypes не поддерживает передачу объединений или структур с битами поля функциям по значению. Хотя это может работать на 32-разрядном x86, это не гарантируется библиотекой в общем случае. Объединения и структуры с битами поля всегда должны передаваться функциям по указателю.
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, ... 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) в списке аргументов функции или как тип поля члена в определении структуры, принимаются только экземпляры ровно того же типа. Есть некоторые исключения из этого правила, когда 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
>>>
Кроме того, если в объявлении argtypes аргумент функции явно объявлен как тип указателя (например, POINTER(c_int)), то в функцию можно передать объект нужного типа (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, ... p = p.next[0] ... foo bar foo bar foo bar foo bar >>>
1.17. Функции обратного вызова
ctypes позволяет создавать указатели на функции C, вызываемые из Python, из вызываемых функций 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)) >>>
Для первой реализации функции обратного вызова мы просто выводим полученные аргументы и возвращаем 0 (инкрементальное развитие ;-):
>>> def py_cmp_func(a, b): ... print "py_cmp_func", a, b ... return 0 ... >>>
Создаём вызываемую функцией C функцию обратного вызова:
>>> cmp_func = CMPFUNC(py_cmp_func) >>>
И мы готовы к работе:
>>> qsort(ia, len(ia), sizeof(c_int), cmp_func) py_cmp_func <ctypes.LP_c_long object at 0x00...> <ctypes.LP_c_long object at 0x00...> py_cmp_func <ctypes.LP_c_long object at 0x00...> <ctypes.LP_c_long object at 0x00...> py_cmp_func <ctypes.LP_c_long object at 0x00...> <ctypes.LP_c_long object at 0x00...> py_cmp_func <ctypes.LP_c_long object at 0x00...> <ctypes.LP_c_long object at 0x00...> py_cmp_func <ctypes.LP_c_long object at 0x00...> <ctypes.LP_c_long object at 0x00...> py_cmp_func <ctypes.LP_c_long object at 0x00...> <ctypes.LP_c_long object at 0x00...> py_cmp_func <ctypes.LP_c_long object at 0x00...> <ctypes.LP_c_long object at 0x00...> py_cmp_func <ctypes.LP_c_long object at 0x00...> <ctypes.LP_c_long object at 0x00...> py_cmp_func <ctypes.LP_c_long object at 0x00...> <ctypes.LP_c_long object at 0x00...> py_cmp_func <ctypes.LP_c_long object at 0x00...> <ctypes.LP_c_long object at 0x00...> >>>
Мы знаем, как получить доступ к содержимому указателя, поэтому давайте переопределим нашу функцию обратного вызова:
>>> def py_cmp_func(a, b): ... print "py_cmp_func", a[0], b[0] ... return 0 ... >>> cmp_func = CMPFUNC(py_cmp_func) >>>
Вот что мы получаем на Windows:
>>> qsort(ia, len(ia), sizeof(c_int), cmp_func) py_cmp_func 7 1 py_cmp_func 33 1 py_cmp_func 99 1 py_cmp_func 5 1 py_cmp_func 7 5 py_cmp_func 33 5 py_cmp_func 99 5 py_cmp_func 7 99 py_cmp_func 33 99 py_cmp_func 7 33 >>>
Забавно видеть, что на Linux функция sort работает намного эффективнее, выполняя меньше сравнений:
>>> 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] ... >>>
Конечный запуск на Windows:
>>> qsort(ia, len(ia), sizeof(c_int), CMPFUNC(py_cmp_func)) py_cmp_func 33 7 py_cmp_func 99 33 py_cmp_func 5 99 py_cmp_func 1 99 py_cmp_func 33 7 py_cmp_func 1 33 py_cmp_func 5 33 py_cmp_func 5 7 py_cmp_func 1 7 py_cmp_func 5 1 >>>
и на Linux:
>>> 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 >>>
Довольно интересно, что функция Windows qsort() требует больше сравнений, чем версия Linux!
Как мы легко можем проверить, наш массив теперь отсортирован:
>>> for i in ia: print i, ... 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.
Цитируя документацию 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, мы можем перебрать его, но мы должны убедиться, что наш цикл завершится, так как у указателей нет размера. Рано или поздно, вероятно, произойдёт ошибка доступа или что-то подобное, поэтому лучше выйти из цикла, когда мы достигнем записи NULL:
>>> for item in table: ... print item.name, item.size ... if item.name is None: ... break ... __hello__ 104 __phello__ -104 __phello__.spam 104 None 0 >>>
Тот факт, что стандартный 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, или номера версии (это формат, используемый для опции линковщика posix-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содержат информацию, указывающую на то, завершился ли вызов функции успешно или нет, а также дополнительный код ошибки. Если возвращаемое значение указывает на ошибку, автоматически генерируется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.Таким образом, это полезно только для прямого вызова функций API Python C.
Все эти классы могут быть созданы, вызывая их с, по крайней мере, одним аргументом, путём к общей библиотеке. Если у вас уже есть дескриптор уже загруженной общей библиотеки, он может быть передан в качестве параметра 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.
New in version 2.6: Параметры use_last_error и use_errno были добавлены.
-
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преобразует строку Unicode, переданную в качестве аргумента, в строку байтов, используя правила преобразования 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.Изменено в версии 2.6: Добавлены необязательные параметры use_errno и use_last_error.
-
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 MessageBoxA так, чтобы она поддерживала параметры по умолчанию и именованные аргументы. Объявление C из файла заголовков Windows такое:
WINUSERAPI int WINAPI
MessageBoxA(
HWND hWnd,
LPCSTR lpText,
LPCSTR lpCaption,
UINT uType);
Вот обертка с помощью ctypes:
>>> from ctypes import c_int, WINFUNCTYPE, windll
>>> from ctypes.wintypes import HWND, LPCSTR, UINT
>>> prototype = WINFUNCTYPE(c_int, HWND, LPCSTR, LPCSTR, UINT)
>>> paramflags = (1, "hwnd", 0), (1, "text", "Hi"), (1, "caption", None), (1, "flags", 0)
>>> MessageBox = prototype(("MessageBoxA", 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 >>>
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), но создание происходит намного быстрее.Добавлена в версии 2.6: Добавлен необязательный аргумент offset.
-
ctypes.cast(obj, type) -
Эта функция аналогична оператору преобразования типов в C. Она возвращает новый экземпляр type, который указывает на тот же блок памяти, что и obj. type должен быть типом указателя, а obj — объектом, который может интерпретироваться как указатель.
-
ctypes.create_string_buffer(init_or_size[, size]) -
Эта функция создаёт изменяемый буфер символов. Возвращаемый объект — массив ctypes типа
c_char.init_or_size должен быть целым числом, определяющим размер массива, или строкой, которая будет использоваться для инициализации элементов массива.
Если в качестве первого аргумента указана строка, буфер увеличивается на один элемент больше, чем длина строки, чтобы последний элемент массива был символом завершения строки NUL. В качестве второго аргумента можно передать целое число, что позволяет указать размер массива, если длина строки не должна использоваться.
Если первый параметр — строка Юникод, она преобразуется в строку 8-битного кода в соответствии с правилами преобразования ctypes.
-
ctypes.create_unicode_buffer(init_or_size[, size]) -
Эта функция создаёт изменяемый буфер символов Юникод. Возвращаемый объект — массив ctypes типа
c_wchar.init_or_size должен быть целым числом, определяющим размер массива, или строкой Юникод, которая будет использоваться для инициализации элементов массива.
Если в качестве первого аргумента указана строка Юникод, буфер увеличивается на один элемент больше, чем длина строки, чтобы последний элемент массива был символом завершения строки NUL. В качестве второго аргумента можно передать целое число, что позволяет указать размер массива, если длина строки не должна использоваться.
Если первый параметр — строка 8-битного кода, она преобразуется в строку Юникод в соответствии с правилами преобразования ctypes.
-
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, или номера версии (этот формат используется для параметра позиционного линковщика-l). Если библиотека не найдена, возвращаетсяNone.Точная функциональность зависит от системы.
Изменено в версии 2.6: Только Windows:
find_library("m")илиfind_library("c")возвращают результат вызоваfind_msvcrt().
-
ctypes.util.find_msvcrt() -
Только Windows: возвращает имя файла библиотеки времени выполнения VC, используемой Python и модулями расширения. Если имя библиотеки определить невозможно, возвращается
None.Если вам нужно освободить память, например, выделенную модулем расширения с помощью вызова
free(void *), важно, чтобы вы использовали функцию в той же библиотеке, что и выделение памяти.Новая в версии 2.6.
-
ctypes.FormatError([code]) -
Только Windows: Возвращает текстовое описание кода ошибки code. Если код ошибки не указан, используется последний код ошибки, полученный вызовом функции Windows GetLastError.
-
ctypes.GetLastError() -
Только Windows: Возвращает последний код ошибки, установленный Windows в потоке вызова. Эта функция вызывает функцию Windows
GetLastError()напрямую, она не возвращает приватную копию кода ошибки ctypes.
-
ctypes.get_errno() -
Возвращает текущее значение приватной копии системной переменной ctypes
errnoв потоке вызова.Новая в версии 2.6.
-
ctypes.get_last_error() -
Только Windows: возвращает текущее значение приватной копии системной переменной ctypes
LastErrorв потоке вызова.Новая в версии 2.6.
-
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_conversion_mode(encoding, errors) -
Эта функция устанавливает правила, которые объекты ctypes используют при преобразовании между строками 8-битного кода и строками Юникод. encoding должна быть строкой, определяющей кодировку, например,
'utf-8'или'mbcs', errors должна быть строкой, определяющей обработку ошибок при кодировании/декодировании. Примерами возможных значений являются"strict","replace", или"ignore".set_conversion_mode()возвращает кортеж из 2 элементов, содержащий предыдущие правила преобразования. В Windows начальными правилами преобразования являются('mbcs', 'ignore'), на других системах('ascii', 'strict').
-
ctypes.set_errno(value) -
Устанавливает текущее значение приватной копии системной переменной ctypes
errnoв потоке вызова на value и возвращает предыдущее значение.Новая в версии 2.6.
-
ctypes.set_last_error(value) -
Только Windows: устанавливает текущее значение приватной копии системной переменной ctypes
LastErrorв потоке вызова на value и возвращает предыдущее значение.Новая в версии 2.6.
-
ctypes.sizeof(obj_or_type) -
Возвращает размер в байтах буфера памяти типа или экземпляра ctypes. Выполняет то же, что и оператор C
sizeof.
-
ctypes.string_at(address[, size]) -
Эта функция возвращает строку, начинающуюся по адресу памяти address. Если указан размер, он используется в качестве размера, иначе строка предполагается нуль-терминированной.
-
ctypes.WinError(code=None, descr=None) -
Только Windows: эта функция, вероятно, наихудшим образом названа в ctypes. Она создаёт экземпляр WindowsError. Если code не указан, вызывается
GetLastErrorдля определения кода ошибки. Еслиdescrне указан, вызываетсяFormatError()для получения текстового описания ошибки.
-
ctypes.wstring_at(address[, size]) -
Эта функция возвращает строку символов Юникод, начинающуюся по адресу памяти address, как строку Юникод. Если size указан, он используется в качестве количества символов строки, иначе строка предполагается нуль-терминированной.
2.6. Типы данных
-
class ctypes._CData -
Этот непубличный класс является общим базовым классом всех типов данных ctypes. Среди прочего, все экземпляры типов ctypes содержат блок памяти, который хранит данные, совместимые с C; адрес блока памяти возвращается функцией-помощником
addressof(). Еще одна переменная экземпляра экспортируется как_objects; она содержит другие объекты Python, которые необходимо поддерживать в живом состоянии, если блок памяти содержит указатели.Общие методы типов данных ctypes, это все методы класса (точнее, методы метакласса метакласса):
-
from_buffer(source[, offset]) -
Этот метод возвращает экземпляр ctypes, который разделяет буфер объекта source. Объект source должен поддерживать интерфейс записи в буфер. Необязательный параметр offset указывает смещение в буфере исходного объекта в байтах; по умолчанию он равен нулю. Если буфер исходного объекта недостаточно велик, возникает
ValueError.Новое в версии 2.6.
-
from_buffer_copy(source[, offset]) -
Этот метод создаёт экземпляр ctypes, копируя буфер из буфера объекта source, который должен быть доступен для чтения. Необязательный параметр offset указывает смещение в буфере исходного объекта в байтах; по умолчанию он равен нулю. Если буфер исходного объекта недостаточно велик, возникает
ValueError.Новое в версии 2.6.
-
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, поэтому наследует их методы и атрибуты.Изменено в версии 2.6: Типы данных ctypes, которые не являются и не содержат указателей, теперь могут быть сериализованы.
У экземпляров есть один атрибут:
-
value -
Этот атрибут содержит фактическое значение экземпляра. Для целых чисел и указателей это целое число, для символьных типов это строка с одним символом, для типов указателей на символы это строка Python или строка Unicode.
При получении атрибута
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.Новое в версии 2.6.
-
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.Новое в версии 2.7.
-
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, и конструктор принимает любой объект, имеющий истинное значение.Новая функция с версии 2.6.
-
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_после объявления класса, определяющего подкласс структуры, это позволяет создавать типы данных, которые напрямую или косвенно ссылаются на себя: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_. -
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/2.7/library/ctypes.html