如何重新加载Python3 C扩展模块?

时间:2011-11-28 12:05:57

标签: module python-3.x reload

我为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的第一个编译版本。

如何使用最新编译版本有效地重新加载扩展模块?

2 个答案:

答案 0 :(得分:4)

您可以使用imp.reload()函数在Python 3.x中重新加载模块。 (此函数曾经是Python 2.x中的内置函数。请务必阅读文档 - 有一些注意事项!)

Python的导入机制永远不会dlclose()共享库。加载后,库将一直保留,直到进程终止。

您的选择(按减少有用性排序):

  1. 将模块导入移动到子进程,并在重新编译后再次调用子进程,即你有一个简单的Python脚本do_stuff.py

    import mycext
    mycext.do_stuff()
    

    并使用

    调用此脚本
    subprocess.call([sys.executable, "do_stuff.py"])
    
  2. 将标题中的编译时常量转换为可以从Python更改的变量,无需重新加载模块。

  3. 删除对模块的所有引用后,手动dlclose()库(由于您自己没有持有所有引用,因此有点脆弱。)

  4. 滚动您自己的导入机制。

    这是一个如何做到这一点的例子。我写了一个最小的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)

还有另一种方法,设置新模块名称,导入它,并更改对它的引用。