ctypes
— Библиотека иностранных функций для Python¶
ctypes
- это библиотека посторонних функций для Python. Она предоставляет типы данных, совместимые с C, и позволяет вызывать функции в DLL или разделяемых библиотеках. Ее можно использовать для обертывания этих библиотек в чистый Python.
учебник по ctypes¶
Примечание: В примерах кода в этом руководстве используется doctest
, чтобы убедиться, что они действительно работают. Поскольку некоторые примеры кода ведут себя по-разному в Linux, Windows или macOS, они содержат директивы doctest в комментариях.
Примечание: Некоторые примеры кода ссылаются на тип ctypes c_int
. На платформах, где sizeof(long) == sizeof(int)
, это псевдоним c_long
. Поэтому не следует путаться, если напечатано c_long
, если вы ожидали c_int
— на самом деле это один и тот же тип.
Загрузка библиотек динамических ссылок¶
ctypes
экспортирует объекты cdll, а в Windows windll и oledll, для загрузки библиотек динамических ссылок.
Вы загружаете библиотеки, обращаясь к ним как к атрибутам этих объектов. cdll загружает библиотеки, которые экспортируют функции, используя стандартное соглашение о вызове cdecl
, в то время как библиотеки windll вызывают функции, используя соглашение о вызове stdcall
. oledll также использует соглашение о вызове stdcall
, и предполагает, что функции возвращают код ошибки Windows HRESULT
. Код ошибки используется для автоматического создания исключения OSError
при неудачном вызове функции.
Изменено в версии 3.3: Раньше ошибки Windows вызывали ошибку WindowsError
, которая теперь является псевдонимом OSError
.
Вот несколько примеров для Windows. Обратите внимание, что msvcrt
является стандартной библиотекой MS, содержащей большинство стандартных функций языка 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 ...>
>>>
Доступ к функциям из загруженных 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
>>>
Функции вызова¶
Вы можете вызывать эти функции, как и любые другие вызываемые функции Python. В этом примере используется функция time()
, которая возвращает системное время в секундах с момента эпохи Unix, и функция GetModuleHandleA()
, которая возвращает хэндл модуля win32.
Этот пример вызывает обе функции с указателем NULL
(в качестве указателя None
следует использовать NULL
):
>>> print(libc.time(None))
1150640792
>>> print(hex(windll.kernel32.GetModuleHandleA(None)))
0x1d000000
>>>
ValueError
возникает, когда вы вызываете функцию stdcall
с соглашением о вызове cdecl
, или наоборот:
>>> cdll.kernel32.GetModuleHandleA(None)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: Procedure probably called with not enough arguments (4 bytes missing)
>>>
>>> windll.msvcrt.printf(b"spam")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: Procedure probably called with too many arguments (4 bytes in excess)
>>>
Чтобы узнать правильное соглашение о вызове, необходимо заглянуть в заголовочный файл языка C или в документацию к функции, которую вы хотите вызвать.
В Windows ctypes
использует структурированную обработку исключений win32 для предотвращения сбоев из-за общих ошибок защиты, когда функции вызываются с недопустимыми значениями аргументов:
>>> windll.kernel32.GetModuleHandleA(32)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
OSError: exception: access violation reading 0x00000020
>>>
Однако существует достаточно способов вывести Python из строя с помощью ctypes
, поэтому в любом случае следует быть осторожным. Модуль faulthandler
может быть полезен при отладке аварий (например, из-за ошибок сегментации, вызванных ошибочными вызовами библиотеки C).
None
, целые числа, байтовые объекты и (unicode) строки - единственные объекты Python, которые могут непосредственно использоваться в качестве параметров в этих вызовах функций. Целые числа None
передаются как указатель C NULL
, байтовые объекты и строки передаются как указатель на блок памяти, содержащий их данные (char* или wchar_t*). Целые числа Python передаются как тип C int, используемый платформой по умолчанию, их значение маскируется, чтобы соответствовать типу C.
Прежде чем перейти к вызову функций с другими типами параметров, мы должны узнать больше о типах данных ctypes
.
Фундаментальные типы данных¶
ctypes
определяет ряд примитивных типов данных, совместимых с Си:
тип ctypes |
Тип C |
Тип Python |
---|---|---|
_Bool |
bool (1) |
|
char |
1-символьный байтовый объект |
|
|
1-символьная строка |
|
char |
int |
|
unsigned char |
int |
|
short |
int |
|
unsigned short |
int |
|
int |
int |
|
unsigned int |
int |
|
long |
int |
|
unsigned long |
int |
|
|
int |
|
unsigned __int64 или unsigned long long |
int |
|
|
int |
|
|
int |
|
float |
float |
|
double |
float |
|
long double |
float |
|
char* (завершается NUL) |
объект байта или |
|
wchar_t* (завершается NUL) |
строка или |
|
void* |
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 неизменяемы):
>>> s = "Hello, World"
>>> c_s = c_wchar_p(s)
>>> print(c_s)
c_wchar_p(139966785747344)
>>> print(c_s.value)
Hello World
>>> c_s.value = "Hi, there"
>>> print(c_s) # the memory location has changed
c_wchar_p(139966783348904)
>>> print(c_s.value)
Hi, there
>>> print(s) # first object is unchanged
Hello, World
>>>
Однако следует быть осторожным, чтобы не передавать их в функции, ожидающие указателей на изменяемую память. Если вам нужны блоки изменяемой памяти, в ctypes есть функция create_string_buffer()
, которая создает их различными способами. Доступ к текущему содержимому блока памяти (или его изменение) можно получить с помощью свойства raw
; если вы хотите получить доступ к нему как к строке, завершенной NUL, используйте свойство value
:
>>> from ctypes import *
>>> p = create_string_buffer(3) # create a 3 byte buffer, initialized to NUL bytes
>>> print(sizeof(p), repr(p.raw))
3 b'\x00\x00\x00'
>>> p = create_string_buffer(b"Hello") # create a buffer containing a NUL terminated string
>>> print(sizeof(p), repr(p.raw))
6 b'Hello\x00'
>>> print(repr(p.value))
b'Hello'
>>> p = create_string_buffer(b"Hello", 10) # create a 10 byte buffer
>>> print(sizeof(p), repr(p.raw))
10 b'Hello\x00\x00\x00\x00\x00'
>>> p.value = b"Hi"
>>> print(sizeof(p), repr(p.raw))
10 b'Hi\x00lo\x00\x00\x00\x00\x00'
>>>
Функция create_string_buffer()
заменяет функцию c_buffer()
(которая все еще доступна как псевдоним), а также функцию c_string()
из более ранних версий ctypes. Для создания изменяемого блока памяти, содержащего символы юникода типа C wchar_t
, используйте функцию create_unicode_buffer()
.
Вызов функций, продолжение¶
Обратите внимание, что printf печатает в настоящий стандартный канал вывода, не в sys.stdout
, поэтому эти примеры будут работать только в приглашении консоли, а не из IDLE или PythonWin:
>>> printf = libc.printf
>>> printf(b"Hello, %s\n", b"World!")
Hello, World!
14
>>> printf(b"Hello, %S\n", "World!")
Hello, World!
14
>>> printf(b"%d bottles of beer\n", 42)
42 bottles of beer
19
>>> printf(b"%f bottles of beer\n", 42.5)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ArgumentError: argument 2: exceptions.TypeError: Don't know how to convert parameter 2
>>>
Как уже упоминалось ранее, все типы Python, кроме целых чисел, строк и байтовых объектов, должны быть обернуты в соответствующий им тип ctypes
, чтобы их можно было преобразовать в требуемый тип данных C:
>>> printf(b"An int %d, a double %f\n", 1234, c_double(3.14))
An int 1234, a double 3.140000
31
>>>
Вызов функций с вашими собственными пользовательскими типами данных¶
Вы также можете настроить преобразование аргументов ctypes
так, чтобы экземпляры ваших собственных классов могли использоваться в качестве аргументов функции. ctypes
ищет атрибут _as_parameter_
и использует его в качестве аргумента функции. Конечно, он должен быть одним из целого числа, строки или байта:
>>> class Bottles:
... def __init__(self, number):
... self._as_parameter_ = number
...
>>> bottles = Bottles(42)
>>> printf(b"%d bottles of beer\n", bottles)
42 bottles of beer
19
>>>
Если вы не хотите хранить данные экземпляра в переменной экземпляра _as_parameter_
, вы можете определить property
, которая делает атрибут доступным по запросу.
Указание необходимых типов аргументов (прототипы функций)¶
Можно указать требуемые типы аргументов функций, экспортируемых из DLL, установив атрибут argtypes
.
argtypes
должна быть последовательностью типов данных C (функция printf
, вероятно, не является хорошим примером здесь, потому что она принимает переменное число и различные типы параметров в зависимости от строки формата, с другой стороны, это довольно удобно для экспериментов с этой функцией):
>>> printf.argtypes = [c_char_p, c_char_p, c_int, c_double]
>>> printf(b"String '%s', Int %d, Double %f\n", b"Hi", 10, 2.2)
String 'Hi', Int 10, Double 2.200000
37
>>>
Указание формата защищает от несовместимых типов аргументов (так же, как прототип для функции C) и пытается преобразовать аргументы к допустимым типам:
>>> printf(b"%d %d %d", 1, 2, 3)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
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_
.
Типы возврата¶
По умолчанию предполагается, что функции возвращают тип C int. Другие типы возврата могут быть заданы установкой атрибута restype
объекта функции.
Вот более продвинутый пример, в нем используется функция strchr
, которая принимает указатель на строку и char, а возвращает указатель на строку:
>>> strchr = libc.strchr
>>> strchr(b"abcdef", ord("d"))
8059983
>>> strchr.restype = c_char_p # c_char_p is a pointer to a string
>>> strchr(b"abcdef", ord("d"))
b'def'
>>> print(strchr(b"abcdef", ord("x")))
None
>>>
Если вы хотите избежать приведенных выше вызовов ord("x")
, вы можете установить атрибут argtypes
, и второй аргумент будет преобразован из односимвольного объекта Python bytes в C char:
>>> strchr.restype = c_char_p
>>> strchr.argtypes = [c_char_p, c_char]
>>> strchr(b"abcdef", b"d")
'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 FormatMessage()
api для получения строкового представления кода ошибки и возвращает исключение. WinError
принимает необязательный параметр кода ошибки, если он не используется, то вызывает GetLastError()
для его получения.
Обратите внимание, что гораздо более мощный механизм проверки ошибок доступен через атрибут errcheck
; подробности см. в справочном руководстве.
Передача указателей (или: передача параметров по ссылке)¶
Иногда функция 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'
>>>
Структуры и союзы¶
Структуры и союзы должны происходить от базовых классов 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, которая содержит две точки с именами верхний левый и нижний правый:
>>> 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))
Поля descriptorмогут быть получены из класса, они полезны для отладки, поскольку могут предоставить полезную информацию:
>>> 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, библиотека не гарантирует, что это будет работать в общем случае. Объединения и структуры с битовыми полями всегда должны передаваться в функции по указателю.
Выравнивание структуры/объединения и порядок байтов¶
По умолчанию поля Structure и Union выравниваются так, как это делает компилятор языка C. Это поведение можно переопределить, указав атрибут класса _pack_
в определении подкласса. Он должен быть установлен в целое положительное число и задает максимальное выравнивание для полей. Это то, что #pragma pack(n)
также делает в MSVC.
ctypes
использует родной порядок байтов для структур и объединений. Для создания структур с неродным порядком байтов можно использовать один из базовых классов BigEndianStructure
, LittleEndianStructure
, BigEndianUnion
и LittleEndianUnion
. Эти классы не могут содержать поля указателей.
Битовые поля в структурах и союзах¶
Можно создавать структуры и объединения, содержащие битовые поля. Битовые поля возможны только для целочисленных полей, ширина бита указывается как третий элемент в кортежах _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>
>>>
Массивы¶
Массивы - это последовательности, содержащие фиксированное количество экземпляров одного типа.
Рекомендуемый способ создания типов массивов - умножение типа данных на целое положительное число:
TenPointsArrayType = POINT * 10
Вот пример несколько искусственного типа данных, структуры, содержащей, помимо прочего, 4 POINT:
>>> from ctypes import *
>>> class POINT(Structure):
... _fields_ = ("x", c_int), ("y", c_int)
...
>>> class MyStruct(Structure):
... _fields_ = [("a", c_int),
... ("b", c_float),
... ("point_array", POINT * 4)]
>>>
>>> print(len(MyStruct().point_array))
4
>>>
Экземпляры создаются обычным способом, путем вызова класса:
arr = TenPointsArrayType()
for pt in arr:
print(pt.x, pt.y)
Приведенный выше код выводит серию строк 0 0
, поскольку содержимое массива инициализировано нулями.
Инициализаторы правильного типа также могут быть указаны:
>>> from ctypes import *
>>> TenIntegers = c_int * 10
>>> ii = TenIntegers(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
>>> print(ii)
<c_long_Array_10 object at 0x...>
>>> for i in ii: print(i, end=" ")
...
1 2 3 4 5 6 7 8 9 10
>>>
Указатели¶
Экземпляры указателей создаются путем вызова функции pointer()
на типе ctypes
:
>>> from ctypes import *
>>> i = c_int(42)
>>> pi = pointer(i)
>>>
Экземпляры указателей имеют атрибут contents
, который возвращает объект, на который указывает указатель, i
объект выше:
>>> pi.contents
c_long(42)
>>>
Обратите внимание, что ctypes
не имеет OOR (возврат исходного объекта), он конструирует новый, эквивалентный объект каждый раз, когда вы получаете атрибут:
>>> 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: Вы можете получить доступ или изменить произвольные области памяти. Обычно вы используете эту возможность только в том случае, если получаете указатель от функции языка Си, и вы знаете, что указатель на самом деле указывает на массив, а не на один элемент.
За кулисами функция 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
>>>
Преобразования типов¶
Обычно ctypes выполняет строгую проверку типов. Это означает, что если у вас есть POINTER(c_int)
в списке argtypes
функции или в качестве типа поля-члена в определении структуры, то принимаются только экземпляры точно такого же типа. Из этого правила есть некоторые исключения, когда ctypes принимает другие объекты. Например, вместо типов указателей можно передавать экземпляры совместимых массивов. Так, для POINTER(c_int)
ctypes принимает массив c_int:
>>> class Bar(Structure):
... _fields_ = [("count", c_int), ("values", POINTER(c_int))]
...
>>> bar = Bar()
>>> bar.values = (c_int * 3)(1, 2, 3)
>>> bar.count = 3
>>> for i in range(bar.count):
... print(bar.values[i])
...
1
2
3
>>>
Кроме того, если аргумент функции явно объявлен как тип указателя (например, POINTER(c_int)
) в argtypes
, в функцию можно передать объект указательного типа (c_int
в данном случае). ctypes применит необходимое преобразование byref()
в этом случае автоматически.
Чтобы задать полю типа POINTER значение NULL
, можно присвоить None
:
>>> bar.values = None
>>>
Иногда встречаются экземпляры несовместимых типов. В языке C вы можете привести один тип к другому типу. ctypes
предоставляет функцию cast()
, которую можно использовать таким же образом. Определенная выше структура Bar
принимает указатели POINTER(c_int)
или массивы c_int
для своего поля values
, но не экземпляры других типов:
>>> bar.values = (c_byte * 4)()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: incompatible types, c_byte_Array_4 instance instead of LP_c_long instance
>>>
Для таких случаев удобна функция cast()
.
Функция cast()
может использоваться для преобразования экземпляра ctypes в указатель на другой тип данных ctypes. cast()
принимает два параметра, объект ctypes, который является или может быть преобразован в указатель какого-либо типа, и тип указателя ctypes. Он возвращает экземпляр второго аргумента, который ссылается на тот же блок памяти, что и первый аргумент:
>>> a = (c_byte * 4)()
>>> cast(a, POINTER(c_int))
<ctypes.LP_c_long object at ...>
>>>
Так, cast()
можно использовать для присвоения полю values
поля Bar
структуры:
>>> bar = Bar()
>>> bar.values = cast((c_byte * 4)(), POINTER(c_int))
>>> print(bar.values[0])
0
>>>
Неполные типы¶
Неполные типы - это структуры, объединения или массивы, члены которых еще не определены. В языке C они задаются прямыми объявлениями, которые определяются позже:
struct cell; /* forward declaration */
struct cell {
char *name;
struct cell *next;
};
Прямой перевод в код ctypes был бы таким, но он не работает:
>>> class cell(Structure):
... _fields_ = [("name", c_char_p),
... ("next", POINTER(cell))]
...
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in cell
NameError: name 'cell' is not defined
>>>
потому что новый class cell
недоступен в самом утверждении класса. В ctypes
мы можем определить класс cell
и установить атрибут _fields_
позже, после оператора class:
>>> from ctypes import *
>>> class cell(Structure):
... pass
...
>>> cell._fields_ = [("name", c_char_p),
... ("next", POINTER(cell))]
>>>
Давайте попробуем. Создадим два экземпляра cell
, и пусть они указывают друг на друга, и, наконец, пройдем по цепочке указателей несколько раз:
>>> c1 = cell()
>>> c1.name = b"foo"
>>> c2 = cell()
>>> c2.name = b"bar"
>>> c1.next = pointer(c2)
>>> c2.next = pointer(c1)
>>> p = c1
>>> for i in range(8):
... print(p.name, end=" ")
... p = p.next[0]
...
foo bar foo bar foo bar foo bar
>>>
Функции обратного вызова¶
ctypes
позволяет создавать указатели вызываемых функций C из вызываемых функций Python. Иногда их называют функциями обратного вызова.
Во-первых, необходимо создать класс для функции обратного вызова. Класс должен знать соглашение о вызове, тип возврата, а также количество и типы аргументов, которые будет получать эта функция.
Функция фабрики CFUNCTYPE()
создает типы для функций обратного вызова, используя соглашение о вызове cdecl
. В Windows фабричная функция WINFUNCTYPE()
создает типы для функций обратного вызова, используя соглашение о вызове stdcall
.
Обе эти фабричные функции вызываются с типом результата в качестве первого аргумента, а в качестве остальных аргументов - ожидаемые типы аргументов функций обратного вызова.
Я приведу пример, который использует функцию qsort()
стандартной библиотеки C, которая используется для сортировки элементов с помощью функции обратного вызова. qsort()
будет использоваться для сортировки массива целых чисел:
>>> IntArray5 = c_int * 5
>>> ia = IntArray5(5, 1, 7, 33, 99)
>>> qsort = libc.qsort
>>> qsort.restype = None
>>>
qsort()
должен быть вызван с указателем на данные для сортировки, количеством элементов в массиве данных, размером одного элемента и указателем на функцию сравнения, обратный вызов. Обратный вызов будет вызван с двумя указателями на элементы, и он должен вернуть отрицательное целое число, если первый элемент меньше второго, ноль, если они равны, и положительное целое число в противном случае.
Поэтому наша функция обратного вызова получает указатели на целые числа и должна возвращать целое число. Сначала создадим type
для функции обратного вызова:
>>> CMPFUNC = CFUNCTYPE(c_int, POINTER(c_int), POINTER(c_int))
>>>
Чтобы начать, вот простой обратный вызов, который показывает значения, которые ему передаются:
>>> def py_cmp_func(a, b):
... print("py_cmp_func", a[0], b[0])
... return 0
...
>>> cmp_func = CMPFUNC(py_cmp_func)
>>>
Результат:
>>> qsort(ia, len(ia), sizeof(c_int), cmp_func)
py_cmp_func 5 1
py_cmp_func 33 99
py_cmp_func 7 33
py_cmp_func 5 7
py_cmp_func 1 7
>>>
Теперь мы можем действительно сравнить два элемента и вернуть полезный результат:
>>> def py_cmp_func(a, b):
... print("py_cmp_func", a[0], b[0])
... return a[0] - b[0]
...
>>>
>>> qsort(ia, len(ia), sizeof(c_int), CMPFUNC(py_cmp_func))
py_cmp_func 5 1
py_cmp_func 33 99
py_cmp_func 7 33
py_cmp_func 1 7
py_cmp_func 5 7
>>>
Как мы можем легко проверить, наш массив теперь отсортирован:
>>> for i in ia: print(i, end=" ")
...
1 5 7 33 99
>>>
Фабрики функций могут быть использованы как фабрики декораторов, поэтому мы можем написать:
>>> @CFUNCTYPE(c_int, POINTER(c_int), POINTER(c_int))
... def py_cmp_func(a, b):
... print("py_cmp_func", a[0], b[0])
... return a[0] - b[0]
...
>>> qsort(ia, len(ia), sizeof(c_int), py_cmp_func)
py_cmp_func 5 1
py_cmp_func 33 99
py_cmp_func 7 33
py_cmp_func 1 7
py_cmp_func 5 7
>>>
Примечание
Убедитесь, что вы сохраняете ссылки на объекты CFUNCTYPE()
до тех пор, пока они используются из кода на языке Си. ctypes
этого не делает, и если вы этого не сделаете, они могут быть собраны в мусор, что приведет к краху вашей программы при выполнении обратного вызова.
Также обратите внимание, что если функция обратного вызова вызывается в потоке, созданном вне контроля Python (например, посторонним кодом, вызывающим обратный вызов), ctypes создает новый фиктивный поток Python при каждом вызове. Такое поведение корректно для большинства целей, но это означает, что значения, сохраненные с помощью threading.local
, не будут не сохраняться при различных обратных вызовах, даже если эти вызовы осуществляются из одного и того же потока C.
Доступ к значениям, экспортированным из dll¶
S […]
ctypes
можно получить доступ к таким значениям с помощью методов класса in_dll()
типа. pythonapi - предопределенный символ, дающий доступ к api Python C:
>>> opt_flag = c_int.in_dll(pythonapi, "Py_OptimizeFlag")
>>> print(opt_flag)
c_long(0)
>>>
Если бы интерпретатор был запущен с -O
, образец вывел бы c_long(1)
, или c_long(2)
, если бы было указано -OO
.
Расширенный пример, который также демонстрирует использование указателей, обращается к указателю PyImport_FrozenModules
, экспортируемому Python.
Цитирую документацию для этого значения:
Этот указатель инициализируется, чтобы указывать на массив записей struct _frozen, заканчивающийся одной, все члены которой
NULL
или ноль. Когда импортируется замороженный модуль, он ищется в этой таблице. Сторонний код может играть с этим, чтобы обеспечить динамически создаваемую коллекцию замороженных модулей.
Поэтому манипуляции с этим указателем могут оказаться даже полезными. Чтобы ограничить размер примера, мы покажем только, как эта таблица может быть прочитана с помощью ctypes
:
>>> from ctypes import *
>>>
>>> class struct_frozen(Structure):
... _fields_ = [("name", c_char_p),
... ("code", POINTER(c_ubyte)),
... ("size", c_int)]
...
>>>
Мы определили тип данных struct _frozen, поэтому можем получить указатель на таблицу:
>>> FrozenTable = POINTER(struct_frozen)
>>> table = FrozenTable.in_dll(pythonapi, "PyImport_FrozenModules")
>>>
Поскольку table
является pointer
к массиву записей struct_frozen
, мы можем выполнять итерации по нему, но мы должны быть уверены, что наш цикл завершится, поскольку указатели не имеют размера. Рано или поздно он, вероятно, завершится с нарушением доступа или чем-то подобным, поэтому лучше выйти из цикла, когда мы попадем на запись NULL
:
>>> for item in table:
... if item.name is None:
... break
... print(item.name.decode("ascii"), item.size)
...
_frozen_importlib 31764
_frozen_importlib_external 41499
__hello__ 161
__phello__ -161
__phello__.spam 161
>>>
Тот факт, что стандартный Python имеет замороженный модуль и замороженный пакет (обозначается отрицательным членом size
), не очень известен, он используется только для тестирования. Попробуйте это, например, с помощью import __hello__
.
Сюрпризы¶
В ctypes
есть некоторые края, где можно ожидать чего-то другого, чем происходит на самом деле.
Рассмотрим следующий пример:
>>> from ctypes import *
>>> class POINT(Structure):
... _fields_ = ("x", c_int), ("y", c_int)
...
>>> class RECT(Structure):
... _fields_ = ("a", POINT), ("b", POINT)
...
>>> p1 = POINT(1, 2)
>>> p2 = POINT(3, 4)
>>> rc = RECT(p1, p2)
>>> print(rc.a.x, rc.a.y, rc.b.x, rc.b.y)
1 2 3 4
>>> # now swap the two points
>>> rc.a, rc.b = rc.b, rc.a
>>> print(rc.a.x, rc.a.y, rc.b.x, rc.b.y)
3 4 3 4
>>>
Хм. Мы, конечно, ожидали, что последний оператор выведет 3 4 1 2
. Что же произошло? Вот шаги строки rc.a, rc.b = rc.b, rc.a
, приведенной выше:
>>> temp0, temp1 = rc.b, rc.a
>>> rc.a = temp0
>>> rc.b = temp1
>>>
Обратите внимание, что temp0
и temp1
являются объектами, все еще использующими внутренний буфер объекта rc
, описанного выше. Поэтому выполнение rc.a = temp0
копирует содержимое буфера temp0
в буфер rc
. Это, в свою очередь, изменяет содержимое temp1
. Таким образом, последнее присваивание rc.b = temp1
не имеет ожидаемого эффекта.
Имейте в виду, что извлечение подобъектов из структур, объединений и массивов не копирует подобъект, вместо этого извлекается объект-обертка, обращающийся к базовому буферу корневого объекта.
Другой пример, поведение которого может отличаться от ожидаемого, следующий:
>>> s = c_char_p()
>>> s.value = b"abc def ghi"
>>> s.value
b'abc def ghi'
>>> s.value is s.value
False
>>>
Примечание
Объекты, инстанцированные из c_char_p
, могут иметь значения только в виде байтов или целых чисел.
Почему он печатает False
? Экземпляры ctypes - это объекты, содержащие блок памяти плюс несколько descriptors, обращающихся к содержимому памяти. Хранение объекта Python в блоке памяти не хранит сам объект, вместо этого хранится contents
объекта. Повторный доступ к содержимому каждый раз создает новый объект Python!
Типы данных переменного размера¶
ctypes
обеспечивает некоторую поддержку массивов и структур переменного размера.
Функция resize()
может быть использована для изменения размера буфера памяти существующего объекта ctypes. В качестве первого аргумента функция принимает объект, а в качестве второго - запрашиваемый размер в байтах. Блок памяти не может быть меньше, чем естественный блок памяти, заданный типом объекта, при попытке изменить размер будет выдано предупреждение ValueError
:
>>> short_array = (c_short * 4)()
>>> print(sizeof(short_array))
8
>>> resize(short_array, 4)
Traceback (most recent call last):
...
ValueError: minimum size is 8
>>> resize(short_array, 32)
>>> sizeof(short_array)
32
>>> sizeof(type(short_array))
8
>>>
Это хорошо и прекрасно, но как получить доступ к дополнительным элементам, содержащимся в этом массиве? Поскольку тип по-прежнему знает только о 4 элементах, мы получаем ошибки при доступе к другим элементам:
>>> short_array[:]
[0, 0, 0, 0]
>>> short_array[7]
Traceback (most recent call last):
...
IndexError: invalid index
>>>
Другой способ использования типов данных переменного размера с ctypes
- это использование динамической природы Python и (повторное) определение типа данных после того, как требуемый размер уже известен, на индивидуальной основе.
справочник типов¶
Иностранные функции¶
Как объяснялось в предыдущем разделе, доступ к внешним функциям можно получить как к атрибутам загруженных общих библиотек. Созданные таким образом объекты функций по умолчанию принимают любое количество аргументов, принимают в качестве аргументов любые экземпляры данных ctypes и возвращают тип результата по умолчанию, заданный загрузчиком библиотеки. Они являются экземплярами частного класса:
-
class
ctypes.
_FuncPtr
¶ Базовый класс для вызываемых на языке 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) результат - это то, что возвращает внешняя функция, как указано атрибутом
restype
.func - это сам объект внешней функции, что позволяет использовать один и тот же вызываемый объект для проверки или пост-обработки результатов нескольких функций.
arguments - кортеж, содержащий параметры, первоначально переданные в вызов функции, это позволяет специализировать поведение на используемых аргументах.
Объект, который возвращает эта функция, будет возвращен из вызова внешней функции, но она также может проверить значение результата и вызвать исключение, если вызов внешней функции не удался.
-
-
-
exception
ctypes.
ArgumentError
¶ Это исключение возникает, когда вызов внешней функции не может преобразовать один из переданных аргументов.
Вызывает auditing event ctypes.seh_exception
с аргументом code
.
Вызывает auditing event ctypes.call_function
с аргументами func_pointer
, arguments
.
Прототипы функций¶
Иностранные функции также могут быть созданы путем инстанцирования прототипов функций. Прототипы функций похожи на прототипы функций в 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
. Функция освобождает GIL во время вызова. use_errno и use_last_error имеют то же значение, что и выше.
-
ctypes.
PYFUNCTYPE
(restype, *argtypes)¶ Возвращаемый прототип функции создает функции, использующие конвенцию вызова Python. Функция не освобождает GIL во время вызова.
Прототипы функций, созданные этими фабричными функциями, могут быть инстанцированы различными способами, в зависимости от типа и количества параметров в вызове:
prototype
(address)Возвращает внешнюю функцию по указанному адресу, который должен быть целым числом.
prototype
(callable)Создайте вызываемую функцию C (функцию обратного вызова) из вызываемой функции Python.
prototype
(func_spec[, paramflags])Возвращает внешнюю функцию, экспортированную разделяемой библиотекой. func_spec должен представлять собой кортеж
(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
, которую должен предоставить вызывающая сторона. Вот декларация на языке Си:
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
>>>
Функции полезности¶
-
ctypes.
addressof
(obj)¶ Возвращает адрес буфера памяти в виде целого числа. obj должен быть экземпляром типа ctypes.
Вызывает auditing event
ctypes.addressof
с аргументомobj
.
-
ctypes.
alignment
(obj_or_type)¶ Возвращает требования к выравниванию типа ctypes. obj_or_type должен быть типом или экземпляром ctypes.
-
ctypes.
byref
(obj[, offset])¶ Возвращает облегченный указатель на obj, который должен быть экземпляром типа ctypes. offset по умолчанию равен нулю и должен быть целым числом, которое будет добавлено к внутреннему значению указателя.
byref(obj, offset)
соответствует такому коду на языке Си:(((char *)&obj) + offset)
Возвращаемый объект может быть использован только в качестве параметра вызова внешней функции. Поведение аналогично
pointer(obj)
, но построение происходит намного быстрее.
-
ctypes.
cast
(obj, type)¶ Эта функция похожа на оператор cast в C. Она возвращает новый экземпляр type, который указывает на тот же блок памяти, что и obj. type должен быть типом указателя, а obj должен быть объектом, который может быть интерпретирован как указатель.
-
ctypes.
create_string_buffer
(init_or_size, size=None)¶ Эта функция создает изменяемый символьный буфер. Возвращаемый объект представляет собой массив ctypes из
c_char
.init_or_size должно быть целым числом, задающим размер массива, или объектом bytes, который будет использоваться для инициализации элементов массива.
Если в качестве первого аргумента указан объект bytes, буфер делается на один элемент больше его длины, так что последним элементом массива является символ завершения NUL. В качестве второго аргумента может быть передано целое число, что позволяет указать размер массива, если длина байтов не должна использоваться.
Вызывает auditing event
ctypes.create_string_buffer
с аргументамиinit
,size
.
-
ctypes.
create_unicode_buffer
(init_or_size, size=None)¶ Эта функция создает изменяемый буфер символов юникода. Возвращаемый объект представляет собой массив ctypes из
c_wchar
.init_or_size должно быть целым числом, задающим размер массива, или строкой, которая будет использоваться для инициализации элементов массива.
Если в качестве первого аргумента указана строка, то буфер создается на один элемент больше длины строки, чтобы последним элементом массива был символ завершения NUL. В качестве второго аргумента может быть передано целое число, что позволяет указать размер массива, если длина строки не должна использоваться.
Вызывает auditing event
ctypes.create_unicode_buffer
с аргументамиinit
,size
.
-
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. Если код ошибки не указан, используется код последней ошибки, вызвав api функцию Windows GetLastError.
-
ctypes.
GetLastError
()¶ Только для Windows: Возвращает последний код ошибки, установленный Windows в вызывающем потоке. Эта функция вызывает функцию Windows GetLastError() напрямую, она не возвращает ctypes-приватную копию кода ошибки.
-
ctypes.
get_errno
()¶ Возвращает текущее значение ctypes-приватной копии системной переменной
errno
в вызывающем потоке.Вызывает auditing event
ctypes.get_errno
без аргументов.
-
ctypes.
get_last_error
()¶ Только для Windows: возвращает текущее значение ctypes-приватной копии системной переменной
LastError
в вызывающем потоке.Вызывает auditing event
ctypes.get_last_error
без аргументов.
-
ctypes.
memmove
(dst, src, count)¶ Аналогична стандартной библиотечной функции C memmove: копирует count байт из src в dst. dst и src должны быть целыми числами или экземплярами ctypes, которые могут быть преобразованы в указатели.
-
ctypes.
memset
(dst, c, count)¶ Аналогична стандартной библиотечной функции C memset: заполняет блок памяти по адресу dst байтами count значения c. dst должно быть целым числом, определяющим адрес, или экземпляром ctypes.
-
ctypes.
POINTER
(type)¶ Эта фабричная функция создает и возвращает новый тип указателя ctypes. Типы указателей кэшируются и повторно используются внутри, поэтому повторный вызов этой функции не требует больших затрат. type должен быть типом ctypes.
-
ctypes.
pointer
(obj)¶ Эта функция создает новый экземпляр указателя, указывающий на obj. Возвращаемый объект имеет тип
POINTER(type(obj))
.Примечание: Если вы просто хотите передать указатель на объект в вызов внешней функции, то следует использовать
byref(obj)
, что гораздо быстрее.
-
ctypes.
resize
(obj, size)¶ Эта функция изменяет размер внутреннего буфера памяти obj, который должен быть экземпляром типа ctypes. Невозможно сделать буфер меньше, чем собственный размер типа объектов, задаваемый
sizeof(type(obj))
, но можно увеличить буфер.
-
ctypes.
set_errno
(value)¶ Установить текущее значение ctypes-приватной копии системной переменной
errno
в вызывающем потоке в значение и вернуть предыдущее значение.Вызывает auditing event
ctypes.set_errno
с аргументомerrno
.
-
ctypes.
set_last_error
(value)¶ Только для Windows: установить текущее значение ctypes-private копии системной переменной
LastError
в вызывающем потоке в значение и вернуть предыдущее значение.Вызывает auditing event
ctypes.set_last_error
с аргументомerror
.
-
ctypes.
sizeof
(obj_or_type)¶ Возвращает размер в байтах буфера памяти типа или экземпляра ctypes. Делает то же самое, что и оператор C
sizeof
.
-
ctypes.
string_at
(address, size=- 1)¶ Эта функция возвращает строку C, начинающуюся по адресу памяти адрес, в виде объекта bytes. Если указан размер, то он используется в качестве размера, в противном случае строка считается завершенной нулем.
Вызывает auditing event
ctypes.string_at
с аргументамиaddress
,size
.
-
ctypes.
WinError
(code=None, descr=None)¶ Только для Windows: эта функция, вероятно, имеет самое неудачное название в ctypes. Она создает экземпляр OSError. Если code не указан, вызывается
GetLastError
для определения кода ошибки. Если descr не указан, вызываетсяFormatError()
для получения текстового описания ошибки.Изменено в версии 3.3: Создается экземпляр
WindowsError
.
-
ctypes.
wstring_at
(address, size=- 1)¶ Эта функция возвращает широкую символьную строку, начинающуюся по адресу памяти адрес, в виде строки. Если указано size, оно используется как количество символов строки, в противном случае предполагается, что строка завершена нулем.
Вызывает auditing event
ctypes.wstring_at
с аргументамиaddress
,size
.
Типы данных¶
-
class
ctypes.
_CData
¶ Этот непубличный класс является общим базовым классом всех типов данных ctypes. Помимо прочего, все экземпляры типа ctypes содержат блок памяти, в котором хранятся данные, совместимые с Си; адрес блока памяти возвращается вспомогательной функцией
addressof()
. Еще одна переменная экземпляра раскрывается как_objects
; она содержит другие объекты Python, которые необходимо поддерживать в актуальном состоянии в случае, если блок памяти содержит указатели.Общие методы типов данных ctypes, это все методы класса (точнее, это методы класса metaclass):
-
from_buffer
(source[, offset])¶ Этот метод возвращает экземпляр ctypes, который разделяет буфер объекта источника. Объект source должен поддерживать интерфейс буфера с возможностью записи. Необязательный параметр offset задает смещение в буфер источника в байтах; по умолчанию он равен нулю. Если буфер источника недостаточно велик, выдается сообщение
ValueError
.Вызывает auditing event
ctypes.cdata/buffer
с аргументамиpointer
,size
,offset
.
-
from_buffer_copy
(source[, offset])¶ Этот метод создает экземпляр ctypes, копируя буфер из буфера объекта источника, который должен быть доступен для чтения. Необязательный параметр offset задает смещение в исходный буфер в байтах; по умолчанию он равен нулю. Если исходный буфер недостаточно велик, выдается сообщение
ValueError
.Вызывает auditing event
ctypes.cdata/buffer
с аргументамиpointer
,size
,offset
.
-
from_address
(address)¶ Этот метод возвращает экземпляр типа ctypes, используя память, указанную address, который должен быть целым числом.
Вызывает auditing event
ctypes.cdata
с аргументом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, которые необходимо поддерживать в живом состоянии, чтобы содержимое блока памяти оставалось актуальным. Этот объект используется только для отладки; никогда не изменяйте содержимое этого словаря.
-
Фундаментальные типы данных¶
-
class
ctypes.
_SimpleCData
¶ Этот непубличный класс является базовым классом всех фундаментальных типов данных ctypes. Он упоминается здесь потому, что содержит общие атрибуты фундаментальных типов данных ctypes.
_SimpleCData
является подклассом_CData
, поэтому наследует их методы и атрибуты. Типы данных ctypes, которые не являются и не содержат указателей, теперь могут быть pickled.Экземпляры имеют один атрибут:
-
value
¶ Этот атрибут содержит фактическое значение экземпляра. Для целочисленных и указательных типов это целое число, для символьных типов - объект или строка размером в один символьный байт, для символьных указателей - объект или строка размером в Python байт.
Когда атрибут
value
извлекается из экземпляра ctypes, обычно каждый раз возвращается новый объект.ctypes
не реализует возврат исходного объекта, всегда строится новый объект. То же самое верно для всех других экземпляров объектов ctypes.
-
Фундаментальные типы данных, когда они возвращаются как результаты вызова внешней функции или, например, при получении членов поля структуры или элементов массива, прозрачно преобразуются в родные типы Python. Другими словами, если внешняя функция имеет restype
из c_char_p
, вы всегда получите объект Python bytes, не экземпляр c_char_p
.
Подклассы фундаментальных типов данных не наследуют это поведение. Таким образом, если внешняя функция restype
является подклассом c_void_p
, вы получите экземпляр этого подкласса из вызова функции. Конечно, вы можете получить значение указателя, обратившись к атрибуту value
.
Это основные типы данных ctypes:
-
class
ctypes.
c_byte
¶ Представляет тип данных C signed char и интерпретирует значение как маленькое целое число. Конструктор принимает необязательный целочисленный инициализатор; проверка на переполнение не производится.
-
class
ctypes.
c_char
¶ Представляет тип данных C char и интерпретирует значение как один символ. Конструктор принимает необязательный инициализатор строки, длина строки должна быть ровно один символ.
-
class
ctypes.
c_char_p
¶ Представляет тип данных C char*, когда он указывает на строку с нулевым окончанием. Для общего символьного указателя, который может также указывать на двоичные данные, следует использовать
POINTER(c_char)
. Конструктор принимает целочисленный адрес или объект bytes.
-
class
ctypes.
c_double
¶ Представляет тип данных C double. Конструктор принимает необязательный инициализатор float.
-
class
ctypes.
c_longdouble
¶ Представляет тип данных C long double. Конструктор принимает необязательный инициализатор float. На платформах, где
sizeof(long double) == sizeof(double)
, является псевдонимомc_double
.
-
class
ctypes.
c_float
¶ Представляет тип данных C float. Конструктор принимает необязательный инициализатор float.
-
class
ctypes.
c_int
¶ Представляет тип данных C signed int. Конструктор принимает необязательный целочисленный инициализатор; проверка на переполнение не производится. На платформах, где
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 32-bit 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 32-bit 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
и интерпретирует значение как односимвольную строку Юникода. Конструктор принимает необязательный инициализатор строки, длина строки должна быть ровно один символ.
-
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*. Вызов этой функции без аргумента создает указатель
NULL
PyObject*.
Модуль ctypes.wintypes
предоставляет некоторые другие типы данных, специфичные для Windows, например, HWND
, WPARAM
или DWORD
. Также определены некоторые полезные структуры, такие как MSG
или RECT
.
Структурированные типы данных¶
-
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
создаст descriptor, которые позволяют читать и записывать поля путем прямого доступа к атрибутам. Это-
_fields_
¶ Последовательность, определяющая поля структуры. Элементы должны быть 2-кортежами или 3-кортежами. Первый элемент - это имя поля, второй элемент определяет тип поля; это может быть любой тип данных ctypes.
Для полей целочисленного типа, таких как
c_int
, может быть задан третий необязательный элемент. Это должно быть небольшое положительное целое число, определяющее битовую ширину поля.Имена полей должны быть уникальными в пределах одной структуры или объединения. Это не проверяется, при повторении имен можно получить доступ только к одному полю.
Можно определить переменную класса
_fields_
после оператора class, определяющего подкласс 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_
.-
Массивы и указатели¶
-
class
ctypes.
Array
(*args)¶ Абстрактный базовый класс для массивов.
Рекомендуемый способ создания конкретных типов массивов - это умножение любого типа данных
ctypes
на целое неотрицательное число. В качестве альтернативы можно создать подкласс этого типа и определить переменные класса_length_
и_type_
. Элементы массива можно читать и записывать, используя стандартный доступ по субскрипту и по срезу; при чтении по срезу результирующий объект не являетсяArray
.-
_length_
¶ Положительное целое число, определяющее количество элементов в массиве. Нестандартные подзаписи приводят к появлению
IndexError
. Будет возвращеноlen()
.
-
_type_
¶ Указывает тип каждого элемента в массиве.
Конструкторы подкласса массива принимают позиционные аргументы, используемые для инициализации элементов по порядку.
-
-
class
ctypes.
_Pointer
¶ Частный, абстрактный базовый класс для указателей.
Конкретные типы указателей создаются путем вызова
POINTER()
с типом, на который будет указывать; это делается автоматически с помощьюpointer()
.Если указатель указывает на массив, то его элементы можно читать и записывать, используя стандартные методы доступа по субскрипту и срезу. Объекты-указатели не имеют размера, поэтому
len()
вызоветTypeError
. Отрицательные подзаписи будут считываться из памяти перед указателем (как в C), а подзаписи вне диапазона, вероятно, приведут к аварийному завершению с нарушением доступа (если вам повезет).-
_type_
¶ Указывает тип, на который указывает.
-
contents
¶ Возвращает объект, на который указывает указатель. Присвоение этого атрибута изменяет указатель на указание на присвоенный объект.
-