如何让Python调用的共享库访问同一Python实例的全局变量?
问题:如何在不修改main.py的前提下让C扩展调用Python主模块的最新全局变量?
代码文件
main.py
#!/usr/bin/python3 import ctypes foo = 1 # 应输出2,但实际输出1 def print_foo(): global foo print(foo) def main(): global foo foo = 2 dll = ctypes.PyDLL("./foo.so") foo = dll.foo foo.restype = None foo() if __name__ == "__main__": main()
foo.c
#define PY_SSIZE_T_CLEAN #include <Python.h> #include <assert.h> #define CHECK_PYTHON_ERROR() {\ if (PyErr_Occurred()) {\ PyErr_Print();\ fprintf(stderr, "Error detected in foo.c:%d\n", __LINE__);\ exit(EXIT_FAILURE);\ }\ } void foo(void) { PyObject *module = PyImport_ImportModule("main"); CHECK_PYTHON_ERROR(); assert(module); PyObject *print_foo = PyObject_GetAttrString(module, "print_foo"); CHECK_PYTHON_ERROR(); assert(print_foo); PyObject *result = PyObject_CallObject(print_foo, NULL); CHECK_PYTHON_ERROR(); assert(result); }
编译与运行
使用以下命令编译C代码为共享库:
gcc foo.c -o foo.so -shared -fPIC -Wall -Wextra -Wpedantic -Wfatal-errors -g -lpython3.13 -I/home/trez/.pyenv/versions/3.13.0/include/python3.13 -L/home/trez/.pyenv/versions/3.13.0/lib -ldl -Wl,-rpath,/home/trez/.pyenv/versions/3.13.0/lib -lm
其中编译参数来自python3.13-config --includes --ldflags的输出。
运行python3.13 main.py后输出1,不符合预期的2。原因是PyImport_ImportModule会重新导入main模块,拿到的是初始状态的全局变量foo=1,而非主程序中已经修改后的foo=2。
尝试的方案及遇到的问题
将PyImport_ImportModule替换为PyImport_AddModule或PyImport_AddModuleRef后,运行时出现如下错误:
AttributeError: module 'main' has no attribute 'print_foo' Error detected in foo.c:20
查看文档可知:
PyImport_AddModule与PyImport_AddModuleRef()类似,但返回借用引用。PyImport_AddModuleRef不会加载或导入模块;若模块未加载,会返回空模块对象。需使用PyImport_ImportModule()或其变体导入模块。
但实际情况并非模块未加载——在assert(module);后添加以下调试代码:
PyObject *g = PyEval_GetGlobals(); CHECK_PYTHON_ERROR(); PyObject *g_repr = PyObject_Repr(g); CHECK_PYTHON_ERROR(); const char *s = PyUnicode_AsUTF8(g_repr); CHECK_PYTHON_ERROR(); printf("s: '%s'\n", s);
运行后输出显示,当前上下文的全局字典中确实存在print_foo函数:
s: '{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x6ffc309a5e00>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': '/home/trez/Programming/ctypes-run-bug/main.py', '__cached__': None, 'ctypes': <module 'ctypes' from '/home/trez/.pyenv/versions/3.13.0/lib/python3.13/ctypes/__init__.py'>, 'foo': <_FuncPtr object at 0x6ffc30946450>, 'print_foo': <function print_foo at 0x6ffc308231a0>, 'main': <function main at 0x6ffc3088f4c0>}' AttributeError: module 'main' has no attribute 'print_foo' Error detected in foo.c:28
推测当前上下文的全局变量来自原Python实例,但PyImport_AddModule("main")未能正确获取到这个实例对应的模块对象。
提问
如何在不修改main.py的前提下,修改foo.c使程序输出2?
内容的提问来源于stack exchange,提问作者MyNameIsTrez
相关产品推荐
相关产品推荐

