我为Python 3.2编写了一个C扩展(mycext.c)。扩展依赖于存储在C头(myconst.h)中的常量数据。头文件由Python脚本生成。在同一个脚本中,我使用了最近编译的模块。 Python3 myscript中的工作流程(未完全显示)如下:
configure_C_header_constants()
write_constants_to_C_header() # write myconst.h
os.system('python3 setup.py install --user') # compile mycext
import mycext
mycext.do_stuff()
这是第一次在Python会话中完美运行。如果我在同一个会话中重复该过程(例如,在单元测试的两个不同的测试用例中),则始终(重新)加载mycext的第一个编译版本。
如何使用最新编译版本有效地重新加载扩展模块?
答案 0 :(得分:4)
您可以使用 imp.reload()
函数在Python 3.x中重新加载模块。 (此函数曾经是Python 2.x中的内置函数。请务必阅读文档 - 有一些注意事项!)
Python的导入机制永远不会dlclose()
共享库。加载后,库将一直保留,直到进程终止。
您的选择(按减少有用性排序):
将模块导入移动到子进程,并在重新编译后再次调用子进程,即你有一个简单的Python脚本do_stuff.py
import mycext
mycext.do_stuff()
并使用
调用此脚本subprocess.call([sys.executable, "do_stuff.py"])
将标题中的编译时常量转换为可以从Python更改的变量,无需重新加载模块。
删除对模块的所有引用后,手动dlclose()
库(由于您自己没有持有所有引用,因此有点脆弱。)
滚动您自己的导入机制。
这是一个如何做到这一点的例子。我写了一个最小的Python C扩展mini.so
,只导出一个名为version
的整数。
>>> import ctypes
>>> libdl = ctypes.CDLL("libdl.so")
>>> libdl.dlclose.argtypes = [ctypes.c_void_p]
>>> so = ctypes.PyDLL("./mini.so")
>>> so.PyInit_mini.argtypes = []
>>> so.PyInit_mini.restype = ctypes.py_object
>>> mini = so.PyInit_mini()
>>> mini.version
1
>>> del mini
>>> libdl.dlclose(so._handle)
0
>>> del so
此时,我在mini.c
中增加版本号并重新编译。
>>> so = ctypes.PyDLL("./mini.so")
>>> so.PyInit_mini.argtypes = []
>>> so.PyInit_mini.restype = ctypes.py_object
>>> mini = so.PyInit_mini()
>>> mini.version
2
您可以看到使用了新版本的模块。
供参考和试验,这里是mini.c
:
#include <Python.h>
static struct PyModuleDef minimodule = {
PyModuleDef_HEAD_INIT, "mini", NULL, -1, NULL
};
PyMODINIT_FUNC
PyInit_mini()
{
PyObject *m = PyModule_Create(&minimodule);
PyModule_AddObject(m, "version", PyLong_FromLong(1));
return m;
}
答案 1 :(得分:0)
还有另一种方法,设置新模块名称,导入它,并更改对它的引用。