Когда выше мы использовали функцию PyRun_String для выполнения выражений и получения результатов, программный код выполнялся в пространстве имен существующего модуля Python. Однако иногда для выполнения строк программного кода удобнее создавать совершенно новое пространство имен, независимое от существующих файлов модулей. Программа на языке C, представленная в примере 20.28, демонстрирует, как это делается. Пространство имен создается как новый объект словаря Python, при этом в процессе участвует ряд новых для нас функций API:
PyDict_New
Создает новый пустой объект словаря.
PyDict_SetItemString
Выполняет присваивание по ключу словаря.
PyDict_GetItemString
Загружает значение из словаря по ключу.
PyRun_String
Выполняет строку программного кода в пространствах имен, как и ранее.
PyEval_GetBuiltins
Получает модуль с областью видимости встроенных элементов.
Главная хитрость здесь заключается в новом словаре. Входные и выходные данные для строк встроенного программного кода отображаются в этот словарь при его передаче в качестве словарей пространств имен кода в вызове функции PyRun_String. В итоге программа на C из примера 20.28 работает в точности, как следующий программный код Python:
>>> d = {}
>>> d[‘Y’] = 2
>>> exec(‘X = 99′, d, d)
>>> exec(‘X = X + Y’, d, d)
>>> print(d[‘X‘])
101
Но здесь каждая операция Python заменяется вызовом C API.
Пример 20.28. PP4E\Integrate\Embed\Basics\embed-dict.c
/* создает новый словарь пространства имен для строки программного кода */
#include <Python.h>
main() { int cval; PyObject *pdict, *pval; printf("embed-dict\n"); Py_Initialize();
/* создать новое пространство имен */
pdict = PyDict_New();
PyDict_SetItemString(pdict, "__builtins__", PyEval_GetBuiltins());
PyDict_SetItemString(pdict, "Y", PyLong_FromLong(2)); /* dict[‘Y’] = 2 */ PyRun_String("X = 99", Py_file_input, pdict, pdict); /* вып. инструкц. */ PyRun_String("X = X+Y", Py_file_input, pdict, pdict); /* то же X и Y */ pval = PyDict_GetItemString(pdict, "X"); /* получить dict[‘X’] */
PyArg_Parse(pval, "i", &cval); /* преобразовать в C */
printf("%d\n", cval); /* результат = 101 */
Py_DECREF(pdict);
Py_Finalize();
После компиляции и выполнения эта программа на C выведет следующее:
…/PP4E/Integrate/Embed/Basics$ ./embed-dict embed-dict
101
На этот раз вывод получился иным: он отражает значение переменной Python X, присвоенное встроенными строками программного кода Python и полученное в C. В целом C может получать атрибуты модуля, либо вызывая функцию PyObject_GetAttrString с объектом модуля, либо обращаясь к словарю атрибутов модуля с помощью PyDict_GetItemString (также можно использовать строки выражений, но не напрямую). В данном случае модуля нет вообще, поэтому для обращения к пространству имен программного кода из C используется доступ к словарю по индексу.
Помимо возможности расчленения пространств имен строк с программным кодом, не зависимых от файлов модулей Python базовой системы, эта схема предоставляет естественный механизм связи. Значения, сохраняемые в новом словаре перед выполнением программного кода, служат входными данными, а имена, которым производится присвоение встроенным программным кодом, впоследствии могут извлекаться из словаря как выходные данные. Например, переменная Y во второй строке ссылается на имя, которому в C присваивается значение 2; значение переменной X присваивается программным кодом Python и извлекается позднее в программе C, как результат вывода.
Здесь есть один тонкий прием, требующий пояснения: словари, используемые в качестве пространств имен выполняемого программного кода, обычно должны содержать ссылку __builtins__ на пространство имен области видимости встроенных объектов, которая устанавливается программным кодом следующего вида:
PyDict_SetItemString(pdict, "__builtins__", PyEval_GetBuiltins());
Это неочевидное требование и обычно для модулей и встроенных компонентов, таких как функция exec, оно обрабатывается самим интерпретатором Python. Однако в случае использования собственных словарей пространств имен мы должны определять эту ссылку вручную, если выполняемый программный код должен иметь возможность ссылаться на встроенные имена. Это требование сохраняется в Python 3.X.
Использованная литература:
Марк Лутц — Программирование на Python, 4-е издание, II том, 2011