You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 02:14:57