справочник по Python. мм isbn 9785932861578 9 785932 861578
Скачать 4.21 Mb.
|
import ctypes >>> libc = ctypes.CDLL(“/usr/lib/libc.dylib”) >>> libc.rand() 16807 >>> libc.atoi(“12345”) 12345 >>> В этом примере напрямую вызываются функции libc.rand() и libc.atoi(), имеющиеся в загруженной библиотеке на языке C. Модуль ctypes предполагает, что все функции принимают аргумент типа int или char * и возвращают результат типа int. Поэтому, несмотря на то, что предыдущие вызовы были выполнены вполне успешно, вызовы других библиотечных функций на языке C могут работать не так, как ожидается. Например: >>> libc.atof(“34.5”) -1073746168 >>> Чтобы устранить эту проблему, можно изменить параметры сигнатуры и особенности вызова любой внешней функции func с помощью следующих атрибутов: func.argtypes Кортеж типов данных, определенных в модуле ctypes (рассказывается ниже) и описывающих входные аргументы функции func. func.restype Тип данных, определенный в модуле ctypes, описывающий возвращаемое значение функции func. Для функций, возвращающих результат типа void, используется значение None. Модуль ctypes 761 func.errcheck Вызываемый объект языка Python, принимающий три аргумента (result, func , args), где result – это значение, возвращаемое внешней функцией, func – ссылка на внешнюю функцию и args – кортеж входных аргументов. Эта функция вызывается после вызова внешней функции и может исполь- зоваться для проверки наличия ошибок и выполнения других действий. Ниже приводится пример исправления ошибки в вызове функции atof(), показанной в предыдущем примере: >>> libc.atof.restype=ctypes.c_double >>> libc.atof(“34.5”) 34.5 >>> ctypes.d_double – это ссылка на предопределенный тип данных. Этот и дру- гие типы данных описываются в следующем разделе. Типы данных В табл. 26.5 перечислены типы данных, объявленные в модуле ctypes, кото- рые могут использоваться в качестве значений атрибутов argtypes и restype внешних функций. Колонка «Значение в языке Python» описывает тип данных языка Python, который соответствует указанному типу данных. Таблица 26.5. Типы данных в модуле ctypes Имя типа в модуле ctypes Тип данных в языке C Значение в языке Python c_bool bool True или False c_bytes signed char Короткое целое число c_char char Одиночный символ c_char_p char * Строка или строка байтов, завер- шающаяся символом NULL c_double double Число с плавающей точкой c_longdouble long double Число с плавающей точкой c_float float Число с плавающей точкой c_int int Целое число c_int8 signed char 8-битное целое число c_int16 short 16-битное целое число c_int32 int 32-битное целое число c_int64 long long 64-битное целое число c_long long Целое число c_longlong long long Целое число c_short short Целое число 762 Глава 26. Расширение и встраивание интерпретатора Python Имя типа в модуле ctypes Тип данных в языке C Значение в языке Python c_size_t size_t Це лое число c_ubyte unsigned char Целое число без знака c_uint unsigned int Целое число без знака c_uint8 unsigned char 8-битное целое число без знака c_uint16 unsigned short 16-битное целое число без знака c_uint32 unsigned int 32-битное целое число без знака c_uint64 unsigned long long 64-битное целое число без знака c_ulong unsigned long Целое число без знака c_ulonglong unsigned long long Целое число без знака c_ushort unsigned short Целое число без знака c_void_p void * Целое число c_wchar wchar_t Одиночный символ Юникода c_wchar_p wchar_t * Строка Юникода, завершающая- ся символом NULL Чтобы создать тип, представляющий указатель языка C, к одному из пере- C, к одному из пере- , к одному из пере- численных типов данных следует применить следующую функцию: POINTER(type) Определяет тип, который является указателем на значение типа type. На- пример, вызов POINTER(c_int) представляет тип данных языка C int *. Чтобы определить тип, представляющий массив фиксированного размера, достаточно умножить существующий тип на количество элементов в мас- сиве. Например, выражение c_int*4 представляет тип данных int[4] в язы- ке C. Чтобы определить тип данных, представляющий структуру или объедине- ние в языке C, необходимо создать класс, производный от одного из базо- C, необходимо создать класс, производный от одного из базо- , необходимо создать класс, производный от одного из базо- вых классов Structure или Union. Внутри определения производного класса необходимо объявить атрибут класса _fields_, описывающий его содержи- мое. _fields_ – это список кортежей из 2 или 3 элементов вида (name, ctype) или (name, ctype, width), где поле name – это идентификатор поля структуры, ctype – класс, описывающий тип поля, и width – целое число, определяющее ширину битового поля. Например, взгляните на следующую структуру на языке C: struct Point { double x, y; }; Описание этой структуры в терминах модуля ctypes имеет следующий вид: Таблица 26.5 (продолжение) Модуль ctypes 763 class Point(Structure): _fields_ = [ (“x”, c_double), (“y”, c_double) ] Вызов внешних функций Чтобы вызвать функцию из библиотеки, достаточно просто обратиться к соответствующему методу с набором аргументов, типы которых совме- стимы с сигнатурой. Для простых типов данных, таких как c_int, c_double и других, можно просто передавать значения соответствующих им типов в языке Python (целые числа, числа с плавающей точкой и другие). Допу- Python (целые числа, числа с плавающей точкой и другие). Допу- (целые числа, числа с плавающей точкой и другие). Допу- скается также передавать экземпляры типов c_int, c_double и подобных им. В случае массивов допускается передавать последовательности языка Py- Py- thon совместимых типов. Чтобы передать внешней функции указатель, необходимо сначала создать экземпляр типа, представляющий значение, на которое будет ссылаться указатель, а затем создать объект указателя с помощью одной из следую- щих функций: byref(cvalue [, offset]) Представляет легковесный указатель на значение cvalue. Аргумент cvalue должен быть экземпляром типа данных, определяемого модулем ctypes. Аргумент offset определяет смещение в байтах, которое следует прибавить к значению указателя. Значение, возвращаемое этой функцией, можно ис- пользовать только в вызовах внешних функций. pointer(cvalue) Создает экземпляр указателя, ссылающегося на значение cvalue. Аргумент cvalue должен быть экземпляром типа данных, определяемого модулем ctypes . Эта функция создает экземпляр типа POINTER, описанного выше. Ниже приводится пример, демонстрирующий, как функции на языке C можно передать аргумент типа double *: dval = c_double(0.0) # Создать экземпляр типа double r = foo(byref(dval)) # Вызвать foo(&dval) ёёё p_dval = pointer(dval) # Создать переменную-указатель r = foo(p_dval) # Вызвать foo(p_dval) ёёё # Проверить значение dval после вызова функции print (dval.value) Следует отметить, что нет никакой возможности создать указатель на зна- чение встроенного типа, такого как int или float. Передача указателей на значения таких типов противоречила бы принципу неизменяемости, если внутри функции на языке C выполнялась бы попытка изменить значение. Атрибут cobj.value экземпляра типа cobj, созданного средствами модуля ctypes , содержит само значение. Например, в предыдущем фрагменте при обращении к атрибуту dval.value возвращается значение с плавающей точ- кой, хранящееся в экземпляре dval класса c_double. 764 Глава 26. Расширение и встраивание интерпретатора Python Чтобы передать функции на языке C структуру, следует создать экземпляр структуры или объединения. Для этого требуется вызвать конструктор ра- нее объявленного класса структуры или объединения StructureType: StructureType(*args, **kwargs) Создает экземпляр StructureType, где под StructureType подразумевается класс, производный от класса Structure или Union. Позиционные аргументы в *args используются для инициализации полей структуры; они должны следовать в том же порядке, в каком они перечислены в атрибуте _fields_. Именованные аргументы в **kwargs используются для инициализации только именованных полей структуры. Альтернативные методы конструирования типов Все экземпляры типов данных модуля ctypes, такие как c_int, POINTER и другие, обладают методами класса, которые могут использоваться для создания экземпляров типов данных ctypes из других объектов, размещае- мых в памяти. ty.from_buffer(source [,offset]) Создает экземпляр типа ty, использующий тот же буфер в памяти, что и объект source. Аргумент source должен быть объектом любого типа, кото- рый поддерживает интерфейс буферов, доступных для записи (например, bytearray , массив объектов array, определяемый модулем array, mmap и так далее). Аргумент offset определяет смещение используемой области в бай- тах относительно начала буфера. ty.from_buffer_copy(source [, offset]) То же, что и ty.from_buffer(), за исключением того, что создает копию буфе- ра, благодаря чему объект source может быть доступен только для чтения. ty.from_address(address) Создает экземпляр типа ty, извлекая значение из области памяти с адресом address , который определяется, как целое число. ty.from_param(obj) Создает экземпляр типа ty из объекта obj на языке Python. Этот метод мо- Python. Этот метод мо- . Этот метод мо- жет принимать только объекты obj, которые могут быть приведены к соот- ветствующему типу. Например, целое число в языке Python может быть преобразовано в экземпляр типа c_int. ty.in_dll(library, name) Создает экземпляр типа ty из переменной в разделяемой библиотеке. В ар- ар- ар- гументе library передается экземпляр загруженной библиотеки, например объект, созданный конструктором класса CDLL. В аргументе name передается имя переменной. Этот метод можно использовать для создания оберток во- переменной. Этот метод можно использовать для создания оберток во- переменной. Этот метод можно использовать для создания оберток во- . Этот метод можно использовать для создания оберток во- Этот метод можно использовать для создания оберток во- круг глобальных переменных, объявленных в библиотеке. Следующий пример демонстрирует, как можно создать ссылку на глобаль- ную переменную int status, объявленную в библиотеке libexample.so. Модуль ctypes 765 libexample = ctypes.CDLL(“libexample.so”) status = ctypes.c_int.in_dll(libexample,”status”) Вспомогательные функции Н иже перечислены вспомогательные функции, объявленные в модуле ctypes : addressof(cobj) Возвращает адрес объекта cobj в памяти в виде целого числа. Аргумент cobj должен быть экземпляром типа из модуля ctypes. alignment(ctype_or_obj) Возвращает целочисленную величину выравнивания для типа, объявлен- ного в модуле ctypes, или объекта. В аргументе ctype_or_obj передается один из типов модуля ctypes или экземпляр такого типа. cast(cobj, ctype) Приводит объект cobj типа, объявленного в модуле ctypes, к новому типу ctype . Этот метод работает только с указателями, поэтому аргумент cobj должен быть указателем или массивом, а аргумент ctype должен быть ти- пом указателя. create_string_buffer(init [, size]) Создает символьный буфер, доступный для записи, в виде массива элемен- тов типа c_char. В аргументе init может передаваться целое число, опреде- ляющее размер буфера, или строка, определяющая начальное содержимое буфера. size – это необязательный аргумент, который определяет размер буфера, когда в аргументе init передается строка. По умолчанию аргумент size получает значение на единицу больше, чем количество символов в init. Строки Юникода преобразуются в строки байтов с использованием коди- ровки по умолчанию. create_unicode_buffer(init [, size]) То же, что и create_string_buffer(), за исключением того, что создает массив элементов типа c_wchar. get_errno() Возвращает текущее значение частной, для модуля ctypes, копии перемен- ной errno. get_last_error() Возвращает текущее значение частной, для модуля ctypes, копии перемен- ной LastError в Windows. memmove(dst, src, count) Копирует count байтов из src в dst. В аргументах src и dst допускается пере- давать целые числа, представляющие адреса значений в памяти или эк- земпляры типов, объявленных в модуле ctypes, которые можно преобразо- вать в указатели. Действует точно так же, как и функция memmove() в стан- дартной библиотеке языка C. 766 Глава 26. Расширение и встраивание интерпретатора Python memset(dst, c, count) Записывает count байтов со значением c в область памяти, начиная с адреса dst . В аргументе dst допускается передавать целые числа или экземпляры типов, объявленных в модуле ctypes. В аргументе c передается целое число в диапазоне 0-255, представляющее значение байта. resize(cobj, size) Изменяет размер внутренней памяти, используемой для представления объекта cobj одного из типов модуля ctypes. Аргумент size определяет но- определяет но- но- но- вый размер в байтах. set_conversion_mode(encoding, errors) Определяет кодировку, которая будет использоваться при преобразовании строк Юникода в строки 8-битовых символов. Аргумент encoding определя- ет название кодировки, такое как ‘utf-8’, а аргумент errors – политику об- работки ошибок кодирования, например: ‘strict’ или ‘ignore’. Возвращает кортеж (encoding, errors) с предыдущими значениями настроек. set_errno(value) Записывает значение в частную, для модуля ctypes, копию системной пере- менной errno. Возвращает предыдущее значение. set_last_error(value) Записывает значение в частную, для модуля ctypes, копию переменной Las- tError в системе Windows и возвращает предыдущее значение. sizeof(type_or_cobj) Возвращает размер типа, объявленного в модуле ctypes, или объекта в бай- тах. string_at(address [, size]) Возвращает строку байтов, представляющую size байтов в памяти, начи- ная с адреса address. Если аргумент size опущен, предполагается, что стро- ка байтов заканчивается символом NULL. wstring_at(address [, size]) Возвращает строку Юникода, представляющую size многобайтовых сим- волов, начиная с адреса address. Если аргумент size опущен, предполагает- ся, что строка байтов заканчивается символом NULL. Пример Следующий пример иллюстрирует применение модуля ctypes для построе- ния интерфейса к набору функций на языке C, использовавшихся в самой первой части этой главы, где описывались особенности создания модулей расширений Python вручную. # example.py ёёё import ctypes _example = ctypes.CDLL(“./libexample.so”) ёёё Модуль ctypes 767 # int gcd(int, int) gcd = _example.gcd gcd.argtypes = (ctypes.c_int, ctypes.c_int) gcd.restype = ctypes.c_int ёёё # int replace(char *s, char olcdh, char newch) _example.replace.argtypes = (ctypes.c_char_p, ctypes.c_char, ctypes.c_char) _example.replace.restype = ctypes.c_int ёёё def replace(s, oldch, newch): sbuffer = ctypes.create_string_buffer(s) nrep = _example.replace(sbuffer,oldch,newch) return (nrep,sbuffer.value) ёёё # double distance(Point *p1, Point *p2) class Point(ctypes.Structure): _fields_ = [ (“x”, ctypes.c_double), (“y”, ctypes.c_double) ] ёёё _example.distance.argtypes = (ctypes.POINTER(Point), ctypes.POINTER(Point)) _example.distance.restype = ctypes.c_double ёёё def distance(a,b): p1 = Point(*a) p2 = Point(*b) return _example.distance(byref(p1),byref(p2)) В общем случае использование модуля ctypes всегда связано с созданием промежуточного уровня на языке той или иной сложности. Например, не- которые функции на языке C могут вызываться непосредственно. Однако иногда может потребоваться реализовать промежуточный уровень, чтобы учесть некоторые особенности программного кода на языке C. Так, как было показано в этом примере, чтобы вызвать функцию replace(), необ- ходимо выполнить дополнительные действия, чтобы учесть тот факт, что функция из библиотеки на языке C изменяет содержимое входного буфера. Для вызова функции distance() потребовалось выполнить дополнительные операции, чтобы создать из кортежей экземпляры класса Point и передать функции указатели на них. Примечание Модуль ctypes обладает огромным количеством дополнительных особенностей, которые не рассматривались здесь. Например, модуль позволяет обращаться к са- мым разным библиотекам в системе Windows и обеспечивает поддержку функций обратного вызова, неполных типов и других особенностей. В электронной доку- ментации можно найти массу примеров, благодаря чему она может служить отлич- ной отправной точкой к дальнейшему изучению модуля. |