Наконец, при вызове объектов функций Python из C вы фактически выполняете откомпилированный байт-код, связанный с объектом (например, телом функции), который обычно создается на этапе импортирования вмещающего модуля. При выполнении строк Python должен предварительно скомпилировать строку. Так как компиляция является медленным процессом, это может повлечь существенные потери времени, если предполагается многократное выполнение строки. Чтобы избежать их, откомпилируйте строку в объект байт-кода, который будет выполняться позднее, с помощью функций API, представленных в примере 20.29.
Py_CompileString
Компилирует строку программного кода, возвращает объект байткода.
PyEval_EvalCode
Выполняет объект скомпилированного байт-кода.
Первая из этих функций принимает флаг режима, обычно передаваемый функции PyRun_String, и второй строковый аргумент, используемый только в сообщениях об ошибках. Вторая функция принимает два словаря пространств имен. Эти две функции API использованы в примере 20.29 для компиляции и выполнения трех строк программного кода Python.
Пример 20.29. PP4E\Integrate\Embed\Basics\embed-bytecode.c
/* предварительная компиляция строк программного кода в объекты байт-кода */
#include <Python.h>
#include <compile.h>
#include <eval.h>
main() { int i; char *cval; PyObject *pcode1, *pcode2, *pcode3, *presult, *pdict; char *codestr1, *codestr2, *codestr3;
printf("embed-bytecode\n");
Py_Initialize();
codestr1 = "import usermod\nprint(usermod.message)"; /* инструкции */ codestr2 = "usermod.transform(usermod.message)"; /* выражение */ codestr3 = "print(‘%d:%d’ % (X, X ** 2), end=’ ‘)"; /* входное знач. X */
/* создать новый словарь пространства имен */ pdict = PyDict_New();
if (pdict == NULL) return -1;
PyDict_SetItemString(pdict, "__builtins__", PyEval_GetBuiltins());
/* скомпилировать строки программного кода в объекты байт—кода */ pcode1 = Py_CompileString(codestr1, "<embed>", Py_file_input); pcode2 = Py_CompileString(codestr2, "<embed>", Py_eval_input); pcode3 = Py_CompileString(codestr3, "<embed>", Py_file_input);
/* выполнить скомпилированный байт-код в пространстве имен словаря */ if (pcode1 && pcode2 && pcode3) {
(void) PyEval_EvalCode((PyCodeObject *)pcode1, pdict, pdict); presult = PyEval_EvalCode((PyCodeObject *)pcode2, pdict, pdict); PyArg_Parse(presult, "s", &cval);
printf("%s\n", cval);
Py_DECREF(presult);
/* выполнить объект байт-кода несколько раз */ for (i = 0; i <= 10; i++) {
PyDict_SetItemString(pdict, "X", PyLong_FromLong(i));
(void) PyEval_EvalCode((PyCodeObject *)pcode3, pdict, pdict);
}
printf("\n");
}
/* освободить использовавшиеся объекты */
Py_XDECREF(pdict);
Py_XDECREF(pcode1);
Py_XDECREF(pcode2);
Py_XDECREF(pcode3);
Py_Finalize();
}
Эта программа объединяет в себе ряд приемов, которые мы уже видели. Например, пространством имен, в котором выполняются скомпилированные строки, является вновь создаваемый словарь (а не существующий объект модуля), а входные данные передаются строкам программного кода в виде предустановленных переменных в пространстве имен. После компиляции и выполнения первая часть вывода программы аналогична предыдущим примерам из этого раздела, но последняя строчка представляет 11-кратное выполнение одной и той же предварительно откомпилированной строки:
…/PP4E/Integrate/Embed/Basics$ embed-bytecode embed-bytecode
The meaning of life…
THE MEANING OF PYTHON…
0:0 1:1 2:4 3:9 4:16 5:25 6:36 7:49 8:64 9:81 10:100
Если ваша система выполняет строки программного кода Python несколько раз, предварительное компилирование их в байт-код позволит получить значительное ускорение. Этот этап не является обязательным в других контекстах, где производится обращение к вызываемым объектам Python, включая типичный случай использования технологии встраивания, представленный в следующем разделе.
Использованная литература:
Марк Лутц — Программирование на Python, 4-е издание, II том, 2011