ctypes
Last updated
Was this helpful?
Last updated
Was this helpful?
ctypes - это библиотека внешних функций для Python. Она предоставляет типы данных, совместимые с C, и позволяет вызывать функции в библиотеках DLL или совместно используемых библиотеках. Его можно использовать для обертывания этих библиотек на чистом Python.
ctypes экспортирует cdll, а в объектах Windows windll и oledll для загрузки библиотек динамической компоновки.
Вы загружаете библиотеки, обращаясь к ним как к атрибутам этих объектов. cdll загружает библиотеки, которые экспортируют функции, используя стандартное соглашение о вызовах cdecl
, в то время как библиотеки windll вызывают функции с использованием соглашения о вызовах stdcall
. oledll также использует соглашение о вызовах stdcall
и предполагает, что функции возвращают код ошибки Windows HRESULT
. Код ошибки используется для автоматического создания исключения OSError при сбое вызова функции.
Изменено в версии 3.3: ошибки Windows, используемые для возникновения ошибки WindowsError, которая теперь является псевдонимом OSError.
Вот несколько примеров для Windows. Обратите внимание, что - это стандартная библиотека C MS, содержащая большинство стандартных функций C и использующая соглашение о вызовах cdecl
:
Windows автоматически добавляет обычный суффикс файла .dll
.
В Linux требуется указать имя файла, включая расширение, для загрузки библиотеки, поэтому доступ к атрибутам не может использоваться для загрузки библиотек. Либо следует использовать метод LoadLibrary ()
загрузчиков dll, либо загрузить библиотеку, создав экземпляр CDLL, вызвав конструктор:
Функции доступны как атрибуты объектов dll:
Обратите внимание, что системные dll win32, такие как kernel32
и user32
, часто экспортируют ANSI, а также UNICODE версии функции. Версия UNICODE экспортируется с добавлением W
к имени, а версия ANSI экспортируется с добавлением A
к имени. Функция Win32 GetModuleHandle
, которая возвращает дескриптор модуля для данного имени модуля, имеет следующий прототип C, и макрос используется для предоставления одного из них как GetModuleHandle
в зависимости от того, определен ли UNICODE или нет:
windll не пытается выбрать один из них волшебным образом, вы должны получить доступ к нужной версии, явно указав GetModuleHandleA
или GetModuleHandleW
, а затем вызвать ее с байтовыми или строковыми объектами соответственно.
Иногда библиотеки DLL экспортируют функции с именами, которые не являются действительными идентификаторами Python, например "??2@YAPAXI@Z"
. В этом случае вы должны использовать getattr () для получения функции:
В Windows некоторые DLL экспортируют функции не по имени, а по порядковому номеру. Доступ к этим функциям можно получить, указав объект dll порядковым номером:
Вы можете вызывать эти функции как любые другие вызываемые Python. В этом примере используется функция time ()
, которая возвращает системное время в секундах с момента появления эпохи Unix, и функция GetModuleHandleA ()
, которая возвращает дескриптор модуля win32.
В этом примере обе функции вызываются с указателем NULL
(None
не следует использовать как указатель NULL
):
ValueError возникает, когда вы вызываете функцию stdcall
с соглашением о вызовах cdecl
, или наоборот:
Чтобы узнать правильное соглашение о вызовах, вам нужно заглянуть в файл заголовка C или в документацию для функции, которую вы хотите вызвать.
В Windows ctypes использует структурированную обработку исключений win32 для предотвращения сбоев из-за общих сбоев защиты, когда функции вызываются с недопустимыми значениями аргументов:
Однако существует достаточно способов остановить Python с помощью ctypes, так что вам в любом случае следует быть осторожными. Модуль обработки ошибок может быть полезен при отладке сбоев (например, из-за ошибок сегментации, вызванных ошибочными вызовами библиотеки C).
None
, целые числа, байтовые объекты и (unicode) строки - единственные собственные объекты Python, которые можно напрямую использовать в качестве параметров в этих вызовах функций. None
передается как указатель C NULL
, байтовые объекты и строки передаются как указатель на блок памяти, содержащий их данные (char *
или wchar_t *
). Целые числа Python передаются как тип C int
по умолчанию для платформ, их значение замаскировано, чтобы соответствовать типу C.
Прежде чем мы перейдем к вызову функций с другими типами параметров, мы должны узнать больше о типах данных ctypes.
ctypes определяет ряд примитивных типов данных, совместимых с C:
Тип ctypes
Тип С
Тип Python
c_bool
_Bool
bool (1)
c_char
char
1-символьный байтовый объект
c_wchar
wchar_t
1-символьная строка
c_byte
char
int
c_ubyte
unsigned char
int
c_short
short
int
c_ushort
unsigned short
int
c_int
int
int
c_uint
unsigned int
int
c_long
long
int
c_ulong
unsigned long
int
c_longlong
__int64 или long long
int
c_ulonglong
unsigned __int64 или unsigned long long
int
c_size_t
size_t
int
c_ssize_t
ssize_t или Py_ssize_t
int
c_float
float
float
c_double
double
float
c_longdouble
long double
float
c_char_p
char * (NUL ограниченный)
байтовый объект или None
c_wchar_p
wchar_t * (NUL ограниченный)
string или None
c_void_p
void *
int или None
Конструктор принимает любой объект со значением истинности.
Все эти типы можно создать, вызвав их с необязательным инициализатором правильного типа и значения:
Поскольку эти типы изменчивы, их значение также можно изменить впоследствии:
Присвоение нового значения экземплярам типов указателей c_char_p, c_wchar_p и c_void_p изменяет место в памяти, на которое они указывают, а не содержимое блока памяти (конечно, нет, потому что объекты байтов Python неизменяемы):
Однако вы должны быть осторожны, чтобы не передавать их функциям, ожидающим указателей на изменяемую память. Если вам нужны изменяемые блоки памяти, ctypes имеет функцию create_string_buffer (), которая создает их различными способами. К текущему содержимому блока памяти можно получить доступ (или изменить) с помощью свойства raw
; если вы хотите получить доступ к нему как к строке с завершающим NUL, используйте свойство value
:
Функция create_string_buffer () заменяет функцию c_buffer ()
(которая все еще доступна как псевдоним), а также функцию c_string ()
из более ранних выпусков ctypes. Чтобы создать изменяемый блок памяти, содержащий символы Юникода типа C wchar_t
, используйте функцию create_unicode_buffer ().
Как уже упоминалось ранее, все типы Python, кроме целых чисел, строк и байтов, должны быть заключены в соответствующий им тип ctypes, чтобы их можно было преобразовать в требуемый тип данных C.
Вы также можете настроить преобразование аргументов ctypes, чтобы в качестве аргументов функции можно было использовать экземпляры ваших собственных классов. ctypes ищет атрибут _as_parameter_
и использует его в качестве аргумента функции. Конечно, это должно быть целое число, строка или байты:
Если вы не хотите хранить данные экземпляра в переменной экземпляра _as_parameter_
, вы можете определить свойство, которое делает атрибут доступным по запросу.
Можно указать требуемые типы аргументов функций, экспортируемых из DLL, установив атрибут argtypes.
argtypes должны быть последовательностью типов данных C (функция printf
, вероятно, здесь не лучший пример, потому что она принимает номер переменной и различные типы параметров в зависимости от строки формата, с другой стороны, это очень удобно, чтобы поэкспериментировать с этой особенностью):
Указание формата защищает от несовместимых типов аргументов (как прототип для функции C) и пытается преобразовать аргументы в допустимые типы:
Если вы определили свои собственные классы, которые вы передаете вызовам функций, вы должны реализовать метод класса from_param ()
, чтобы они могли использовать их в последовательности argtypes. Метод класса from_param ()
получает объект Python, переданный в вызов функции, он должен выполнить проверку типа или что-то еще, что необходимо, чтобы убедиться, что этот объект приемлем, а затем вернуть сам объект, его атрибут _as_parameter_
или все, что вы хотите передать в качестве аргумента функции C. Опять же, результатом должно быть целое число, строка, байты, экземпляр ctypes или объект с атрибутом _as_parameter_
.
По умолчанию предполагается, что функции возвращают тип C int. Другие возвращаемые типы могут быть указаны путем установки атрибута restype объекта функции.
Вот более сложный пример, он использует функцию strchr
, которая ожидает указатель на строку и символ и возвращает указатель на строку:
Если вы хотите избежать вышеупомянутых вызовов ord ("x")
, вы можете установить атрибут argtypes, и второй аргумент будет преобразован из односимвольного объекта байтов Python в символ C char:
Вы также можете использовать вызываемый объект Python (например, функцию или класс) в качестве атрибута restype, если внешняя функция возвращает целое число. Вызываемый объект будет вызываться с целым числом, возвращаемым функцией C, и результат этого вызова будет использоваться как результат вызова вашей функции. Это полезно для проверки возвращаемых значений ошибок и автоматического создания исключения:
WinError
- это функция, которая вызывает API-интерфейс Windows FormatMessage ()
для получения строкового представления кода ошибки и возвращает исключение. WinError
принимает необязательный параметр кода ошибки, если никто не используется, он вызывает GetLastError () для его получения.
Обратите внимание, что через атрибут errcheck доступен гораздо более мощный механизм проверки ошибок; подробности см. в справочном руководстве.
Иногда API функции C ожидает указатель pointer на тип данных в качестве параметра, возможно, для записи в соответствующее место или если данные слишком велики для передачи по значению. Это также известно как передача параметров по ссылке.
ctypes экспортирует функцию byref (), которая используется для передачи параметров по ссылке. Тот же эффект может быть достигнут с помощью функции pointer (), хотя pointer () выполняет гораздо больше работы, поскольку создает реальный объект-указатель, поэтому быстрее использовать byref (), если вам не нужен сам объект-указатель в Python:
Структуры и объединения должны быть производными от базовых классов Structure и Union, которые определены в модуле ctypes. Каждый подкласс должен определять атрибут_fields_. _fields_ должен быть списком из двух кортежей, содержащим имя поля field и тип поля type.
Тип поля должен быть типом ctypes, например c_int, или любым другим производным типом ctypes: структурой, объединением, массивом, указателем.
Вот простой пример структуры POINT, которая содержит два целых числа с именами x и y, а также показывает, как инициализировать структуру в конструкторе:
Однако вы можете строить гораздо более сложные конструкции. Структура может сама содержать другие структуры, используя структуру как тип поля.
Вот структура RECT, которая содержит две POINT с именами upperleft и lowerright:
Вложенные структуры также можно инициализировать в конструкторе несколькими способами:
Дескрипторы полей могут быть получены из класса, они полезны для отладки, поскольку могут предоставить полезную информацию:
ctypes не поддерживает передачу объединений или структур с битовыми полями в функции по значению. Хотя это может работать на 32-битной x86, библиотека не гарантирует работу в общем случае. Объединения и структуры с битовыми полями всегда должны передаваться в функции по указателю.
По умолчанию поля Structure и Union выравниваются так же, как это делает компилятор C. Это поведение можно изменить, указав атрибут класса_pack_ в определении подкласса. Это должно быть положительное целое число и указывает максимальное выравнивание для полей. Это то, что #pragma pack(n)
также делает в MSVC.
ctypes использует собственный порядок байтов для структур и объединений. Чтобы построить структуры с неродным порядком байтов, вы можете использовать один из базовых классов BigEndianStructure, LittleEndianStructure, BigEndianUnion и LittleEndianUnion. Эти классы не могут содержать поля указателей.
Можно создавать структуры и объединения, содержащие битовые поля. Битовые поля возможны только для целочисленных полей, разрядность указывается в третьем элементе кортежей_fields_:
Массивы - это последовательности, содержащие фиксированное количество экземпляров одного типа.
Рекомендуемый способ создания типов массивов - умножение типа данных на положительное целое число:
Вот пример несколько искусственного типа данных, структура, содержащая, среди прочего, 4 POINT:
Экземпляры создаются обычным способом, вызывая класс:
Приведенный выше код выводит серию из 0 0
строк, поскольку содержимое массива инициализировано нулями.
Также могут быть указаны инициализаторы правильного типа:
Экземпляры указателя создаются путем вызова функции pointer () для типа ctypes:
Экземпляры указателей имеют атрибут содержимого, который возвращает объект, на который указывает указатель, объект i
выше:
Обратите внимание, что ctypes не имеет OOR (возврат исходного объекта), он создает новый эквивалентный объект каждый раз, когда вы извлекаете атрибут:
Назначение другого экземпляра c_int атрибуту содержимого указателя приведет к тому, что указатель будет указывать на то место памяти, где он хранится:
Экземпляры указателя также можно индексировать целыми числами:
Присвоение целочисленному индексу изменяет указанное значение:
Также можно использовать индексы, отличные от 0, но вы должны знать, что делаете, как и в C: вы можете получить доступ к произвольным ячейкам памяти или изменить их. Обычно вы используете эту функцию, только если вы получаете указатель от функции C, и вы знаете, что указатель фактически указывает на массив, а не на отдельный элемент.
За кулисами функция pointer () делает больше, чем просто создает экземпляры указателя, она должна сначала создать типы types указателей. Это делается с помощью функции POINTER (), которая принимает любой тип ctypes и возвращает новый тип:
Вызов типа указателя без аргумента создает указатель NULL
. Указатели NULL
имеют логическое значение False
:
ctypes проверяет NULL
при разыменовании указателей (но разыменование недопустимых указателей, отличных от NULL
, приведет к сбою Python):
Доступ к стандартной библиотеке C через cdll.msvcrt
будет использовать устаревшую версию библиотеки, которая может быть несовместима с той, которая используется Python. По возможности используйте встроенные функции Python или импортируйте и используйте модуль .
Обратите внимание, что printf
печатает в реальном стандартном канале вывода, а не в , поэтому эти примеры будут работать только в приглашении консоли, а не из IDLE или PythonWin: