Использование классов Python в программах C

ispolzovanie klassov python v programmah c Интеграция Python/C

Ранее в этой главе мы научились использовать классы C++ в Python, создавая для них обертки с помощью SWIG. А можно ли пойти в обратном направлении — использовать классы Python из других языков? Оказывается, для этого нужно только применить уже продемонстрированные интерфейсы.

Напомню, что сценарии Python создают объекты экземпляров классов путем вы зо ва объектов классов, как если бы они являлись функциями. Чтобы сделать это из C (или C++), нужно просто сделать те же шаги: импортировать класс из модуля, создать кортеж аргументов и вызвать класс для создания экземпляра с помощью тех же инструментов C API, которыми вызываются функции Python. Создав экземпляр, можно получать его атрибуты и методы теми же средствами, с помощью которых извлекаются значения глобальных переменных из модуля. Доступ к вызываемым объектам и атрибутам везде выполняется одинаково.

Чтобы продемонстрировать это на практике, в примере 20.33 определен модуль с простым классом Python, которым можно пользоваться в C.

Пример 20.33. PP4E\Integrate\Embed\Pyclasss\module.py

# вызывать этот класс из C для создания объектов

class klass:

def method(self, x, y):

return "brave %s %s" % (x, y) # вызывать из C

Проще некуда, но достаточно для иллюстрации основ. Как обычно, этот модуль должен находиться в пути поиска модулей Python (например, в текущем каталоге или содержащемся в PYTHONPATH), иначе импортировать его при вызове из C не удастся, равно как и из сценария Python. Как вы уже наверняка знаете, раз уж дочитали книгу до этого места, к этому классу Python можно обратиться из программы на языке Python, как показано ниже:

\PP4E\Integrate\Embed\Pyclass$ python

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

>>> object = module.klass() # создать экземпляр класса

>>> result = object.method(‘sir’, ‘robin’) # вызвать метод класса

>>> print(result) brave sir robin

В Python это весьма просто. Те же действия можно выполнить и в C, но для этого потребуется немного больше программного кода. Программа на C, представленная в примере 20.34, выполняет эти шаги, организуя вызовы соответствующих инструментов Python API.

Пример 20.34. PP4E\Integrate\Embed\Pyclass\objects.c

#include <Python.h>

#include <stdio.h>

main() {

/* запускает объекты, используя низкоуровневые вызовы */ char *arg1="sir", *arg2="robin", *cstr;

PyObject *pmod, *pclass, *pargs, *pinst, *pmeth, *pres;

/* экземпляр = module.klass() */

Py_Initialize();

pmod = PyImport_ImportModule("module"); /* получить модуль */

pclass = PyObject_GetAttrString(pmod, "klass"); /* получить класс */ Py_DECREF(pmod);

pargs = Py_BuildValue("()");

pinst = PyEval_CallObject(pclass, pargs); /* вызвать class() */ Py_DECREF(pclass);

Py_DECREF(pargs);

/* результат = instance.method(x,y) */

pmeth = PyObject_GetAttrString(pinst, "method"); /* связанный метод */ Py_DECREF(pinst);

pargs = Py_BuildValue("(ss)", arg1, arg2); /* преобразовать в Python */ pres = PyEval_CallObject(pmeth, pargs); /* вызвать method(x,y) */

Py_DECREF(pmeth);

Py_DECREF(pargs);

PyArg_Parse(pres, "s", &cstr); /* преобразовать в C */

printf("%s\n", cstr);

Py_DECREF(pres);

}

Просмотрите этот файл и разберитесь в деталях. Нужно просто понять, как задача решалась бы в Python, а затем вызывать эквивалентные функции C в Python API. Для компиляции этого исходного текста в выполняемую программу C запустите make-файл в каталоге, где находится этот файл (он аналогичен уже рассмотренным make-файлам). После компиляции запустите, как любую другую программу C:

/PP4E/Integrate/Embed/Pyclass$ ./objects brave sir robin

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

Конечно, на практике этот пример был бы сложнее. Как уже говорилось, обычно нужно проверять значения, возвращаемые всеми вызовами Python API, чтобы убедиться в их успешном выполнении. Например, попытка импортировать модуль в этом программном коде C может закончиться неудачей, если модуль находится вне пути поиска. Если не перехватывать возврат указателя NULL, то программа почти наверняка закончится крахом при попытке использовать этот указатель (когда он понадобится). В примере 20.35 представлен программный код из примера 20.34, в который была добавлена проверка всех ошибок — программный код получился объемным, но надежным.

Пример 20.35. PP4E\Integrate\Embed\Pyclasss\objects-err.c

#include <Python.h>

#include <stdio.h>

#define error(msg) do { printf("%s\n", msg); exit(1); } while (1)

main() {

/* запускает объекты, используя низкоуровневые вызовы и проверяя ошибки */

char *arg1="sir", *arg2="robin", *cstr;

PyObject *pmod, *pclass, *pargs, *pinst, *pmeth, *pres;

/* экземпляр = module.klass() */

Py_Initialize();

pmod = PyImport_ImportModule("module"); /* получить модуль */

if (pmod == NULL)

error("Can’t load module");

pclass = PyObject_GetAttrString(pmod, "klass"); /* получить класс */ Py_DECREF(pmod);

if (pclass == NULL) error("Can’t get module.klass");

pargs = Py_BuildValue("()"); if (pargs == NULL) {

Py_DECREF(pclass);

error("Can’t build arguments list");

}

pinst = PyEval_CallObject(pclass, pargs); /* вызвать class() */

Py_DECREF(pclass);

Py_DECREF(pargs);

if (pinst == NULL) error("Error calling module.klass()");

/* результат = instance.method(x,y) */

pmeth = PyObject_GetAttrString(pinst, "method"); /* связанный метод */ Py_DECREF(pinst);

if (pmeth == NULL)

error("Can’t fetch klass.method");

pargs = Py_BuildValue("(ss)", arg1, arg2); /* преобразовать в Python */ if (pargs == NULL) {

Py_DECREF(pmeth);

error("Can’t build arguments list");

}

pres = PyEval_CallObject(pmeth, pargs); /* вызвать method(x,y) */ Py_DECREF(pmeth);

Py_DECREF(pargs);

if (pres == NULL)

error("Error calling klass.method");

if (!PyArg_Parse(pres, "s", &cstr)) /* преобразовать в C */

error("Can’t convert klass.method result");

printf("%s\n", cstr);

Py_DECREF(pres);

}

Эти 53 строки программного кода на C (не считая make-файла) получают тот же результат, что и 4 строки в интерактивной оболочке Python, которые мы видели выше, — не самый лучший показатель с точки зрения производительности разработчика! Но как бы то ни было, использованная модель позволяет программам на C и C++ использовать Python так же, как программы на Python могут использовать C и C++. В заключении к этой книге, которое следует чуть ниже, я отмечу, что такие комбинации часто могут оказаться более мощными, чем отдельные части, составляющие их.

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

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

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