Простой модуль расширения Python на C

prostoj modul rasshireniya python na c Интеграция Python/C

Такова история вкратце. Чтобы создать модуль на C, необходимо писать программный код на языке C, а чтобы создать тип на C, требуется рассказать о многом таком, что невозможно уместить в этой главе. Эта книга не ставит перед собой целью дать вам знания о программировании на языке C, если у вас их еще нет, тем не менее, для придания конкретности вышесказанному нам придется обратиться к программному коду. Поскольку модули на C проще, а типы C обычно экспортируют модуль C с функцией конструктора экземпляра, начнем изучение основ программирования модулей C с короткого примера.

Как уже упоминалось, при добавлении новых или существующих компонентов C в Python необходимо написать на C слой интерфейсной («связующей») логики, обеспечивающий межъязыковую диспетчеризацию и трансляцию данных. Файл с исходным программным кодом на C, представленный в примере 20.1, демонстрирует, как написать такой слой вручную. В нем реализован простой модуль расширения на C с именем hello для использования в сценариях Python, содержащий функцию с именем message, которая просто возвращает передаваемую ей в качестве аргумента строку вместе с добавленным в ее начало некоторым текстом. Сценарии Python смогут вызывать эту функцию, как обычно, но она написана на C, а не на Python.

Пример 20.1. PP4E\Integrate\Extend\Hello\hello.c

/********************************************************************

*   Простой модуль расширения на C для Python с именем "hello";

*   скомпилируйте его в файл ".so" где-нибудь в пути поиска python,

*   импортируйте и вызовите функцию hello.message;

********************************************************************/

#include <Python.h>

#include <string.h>

/* функции модуля */

static PyObject * /* возвращаемый объект */

message(PyObject *self, PyObject *args) /* self не использ. в модулях */ { /* args — из Python */

char *fromPython, result[1024];

if (! PyArg_Parse(args, "(s)", &fromPython)) /* преобраз. Python -> C */ return NULL; /* пи11=исключение */

else {

strcpy(result, "Hello, "); /* создать строку C */

strcat(result, fromPython); /* добавить строку из Python */

return Py_BuildValue("s", result); /* преобразовать C -> Python */

}

}

/* таблица регистрации */

static PyMethodDef hello_methods[] = {

{"message", message, METH_VARARGS, "func doc"}, /* имя, адрес функции */ /* формат, описание */

{NULL, NULL, 0, NULL} /* признак конца таблицы */

};

/* структура определения модуля */

static struct PyModuleDef hellomodule = {

PyModuleDef_HEAD_INIT,

"hello", /* имя модуля */

"mod doc", /* описание модуля, может быть NULL */

-1, /* размер структуры для каждого экземпляра, -1=глоб. перем. */

hello_methods /* ссылка на таблицу методов */

};

/* инициализация модуля */

PyMODINIT_FUNC

PyInit_hello() /* вызывается первой инструкцией импорта */

{ /* имя имеет значение при динамической загрузке */

return PyModule_Create(&hellomodule);

}

Этот модуль на C имеет стандартную структуру, состоящую из 4 частей, описанных в комментариях, которой следуют все модули на C и которая заметно изменилась в Python 3.X. В конечном итоге, программный код Python вызовет функцию message из этого файла C, передав объект строки и получив обратно новый объект строки. Сначала, однако, нужно как-то скомпоновать файл с интерпретатором Python. Для использования этого файла C в сценарии Python скомпилируйте его в динамически загружаемый объектный файл (например, hello.so в Linux или hello.dll в Cygwin для Windows) с помощью make-файла, такого как в примере 20.2, и поместите полученный объектный файл в каталог, присутствующий в пути поиска модулей, так же, как это делается для файлов .py или .pyc.

Пример 20.2. PP4E\Integrate\Extend\Hello\makefile.hello

################################################################# # Компилирует файл hello.c в разделяемый объектный файл в Cygwin, # динамически загружаемый при первом импорте в Python.

#################################################################

PYLIB = /usr/local/bin

PYINC = /usr/local/include/python3.1

hello.dll: hello.c

gcc hello.c -g -I$(PYINC) -shared -L$(PYLIB) -lpython3.1 -o hello.dll clean:

rm -f hello.dll core

Это make-файл для Cygwin, использующий gcc для компиляции программного кода на C в Windows. На других платформах make-файлы аналогичны, но могут отличаться в деталях. Как рассказывалось в главе 5, Cygwin — это Unix-подобная оболочка и комплект библиотек для Windows. Для работы с примерами в этой главе вам необходимо либо установить Cygwin в свою систему Windows, либо изменить make-файлы в соответствии с требованиями вашего компилятора и платформы. Не забудьте включить путь к каталогу установки Python с помощью флага -I, чтобы обеспечить доступ к заголовочным файлам Python, а также путь к двоичным файлам библиотеки Python с помощью флага -L, если это необходимо. Я использовал имена каталогов для моей установки Python 3.1 на моем ноутбуке после сборки из исходных текстов. Обратите также внимание, что для оформления отступов в make-файлах следует использовать символы табуляции, если вы собираетесь копировать их содержимое из электронной версии книги, где они заменяются пробелами.

Теперь, чтобы задействовать make-файл из примера 20.2 для сборки модуля расширения из примера 20.1, просто введите в оболочке стандартную команду make (в этом примере используется оболочка Cygwin и я перенес одну строку для большей ясности):

/PP4E/Integrate/Extend/Hello$ make -f makefile.hello

gcc hello.c -g -I/usr/local/include/python3.1 -shared -L/usr/local/bin -lpython3.1 -o hello.dll

Эта команда создаст разделяемый объектный файл — библиотеку .dll в Cygwin для Windows. При компиляции таким способом Python автоматически будет загружать и компоновать модуль C при первом импорте сценарием Python. К моменту импортирования двоичный файл библиотеки .dll должен находиться в каталоге, расположенном в пути поиска модулей Python, подобно обычным файлам .py. Поскольку при импорте интерпретатор Python всегда начинает поиск с текущего рабочего каталога, примеры из этой главы будут работать, если запускать их из того же каталога, где выполнялась компиляция (.), без необходимости копировать или перемещать файлы. В крупных системах обычно требуется помещать скомпилированные расширения в каталог, перечисленный в переменной окружения PYTHONPATH или в файлах .pth, или использовать утилиту distutils Python для установки их в подкаталог site-packages в стандартной библиотеке.

Наконец, чтобы вызвать функцию C из программы Python, просто импортируйте модуль hello и вызовите его функцию hello.message со строкой — обратно вы получите обычную строку Python:

…/PP4E/Integrate/Extend/Hello$ python

>>> import hello # импортировать модуль C

>>> hello.message(‘world’) # вызвать функцию C

‘Hello, world’

>>> hello.message(‘extending’)

Hello, extending

Вот и все — вы только что вызвали из Python функцию интегрированного модуля C. Самое важное, на что нужно обратить внимание, — это то, что функция C выглядит точно так, как если бы она была написана на Python. Вызывающая программа на языке Python отправляет и получает при вызове обычные строковые объекты — интерпретатор Python осуществляет маршрутизацию вызова функции C, а функция C сама справляется с преобразованием данных Python/C.

На самом деле мало что выдает в hello модуль расширения на C, за исключением имени файла. Программный код на языке Python импортирует модуль и загружает его атрибуты, как если бы он был написан на Python. Модули расширений на C даже отвечают на вызовы функции dir, как обычно, и обладают стандартными атрибутами модуля и имени файла, хотя имя файла в этом случае не оканчивается на .py или .pyc — единственный очевидный признак, позволяющий сказать, что это библиотека на C:

>  >> dir(hello) # атрибуты модуля на C

[‘__doc__’, ‘__file__’, ‘__name__’, ‘__package__’, ‘message’]

>  >> hello.__name__, hello.__file__ (‘hello’, ‘hello.dll’)

>  >> hello.message # объект функции на C

>  built-in function message>

>  >> hello # объект модуля на C

<module ‘hello’ from ‘hello.dll’>

>  >> hello.__doc__ # строка документирования

mod doc # в программном коде C

>  >> hello.message.__doc__ ‘func doc’

>  >> hello.message() # ошибки тоже обрабатываются

TypeError: argument must be sequence of length 1, not 0 (TypeError: аргумент должен быть последовательностью с длиной 1, а не 0)

Подобно любому модулю Python к расширению на C можно обращаться из файла сценария. Сценарий на языке Python, представленный в примере 20.3, импортирует и использует модуль расширения на C из примера 20.1.

Пример 20.3. PP4E\Integrate\Extend\Hello\hellouse.py "импортирует и использует модуль расширения на C"

import hello

print(hello.message(‘C’))

print(hello.message(‘module ‘ + hello.__file__))

for i in range(3):

reply = hello.message(str(i)) print(reply)

Этот сценарий можно запускать, как всякий другой, — при первом импорте модуля hello интерпретатор Python автоматически найдет объектный файл .dll модуля в каталоге, входящем в путь поиска, и динамически включит его в процесс. Весь вывод этого сценария представляет строки, возвращаемые функцией C из файла hello.c:

/PP4E/Integrate/Extend/Hello$ python hellouse.py

Hello, C

Hello, module /cygdrive/c//PP4E/Integrate/Extend/Hello/hello.dll

Hello, 0

Hello, 1

Hello, 2

Дополнительные сведения о программировании модулей на языке C, а также советы по компиляции и компоновке вы найдете в стандартных руководствах по языку Python. В качестве альтернативы использованию make-файлов обратите также внимание на файлы disthello.py и disthello-alt.py в пакете с примерами. Ниже приводится краткое описание исходного программного кода первого из них:

>  для сборки: python disthello.py build

>  получившаяся библиотека dll появится в подкаталоге build

from distutils.core import setup, Extension setup(ext_modules=[Extension(‘hello’, [‘hello.c’])])

Это сценарий на языке Python, который выполняет компиляцию расширения на C с помощью пакета инструментов distutils — стандартного компонента Python, используемого для сборки, установки и распространения расширений Python, написанных на языке Python или C. Основная задача пакета distutil состоит в том, чтобы автоматизировать сборку и установку пакетов дистрибутивов переносимым способом, но он также знает, как компилировать расширения на C. Обычно в состав дистрибутивов включается файл setup.py, который выполняет установку в подкаталог site-packages стандартной библиотеки. К сожалению, пакет distutils слишком крупный, чтобы его можно было использовать в этой главе — дополнительные подробности о нем смотрите в двух его руководствах в наборе руководств по языку Python.

Использованная литература:

Марк Лутц — Программирование на Python, 4-е издание, II том, 2011

Каталог сайтов Всего.ру
Оцените статью
Секреты программирования
Добавить комментарий