PyImport_ImportModule() 的性能如何,使用它的最佳实践是什么?

What's the performance of PyImport_ImportModule(), and what are the best practices for using it?

我有一个内部循环 C 函数,它(在我的例子中)构造了一个 Python datetime 对象:

PyObject* my_inner_loop_fn(void* some_data) {
    PyObject* datetime = PyImport_ImportModule("datetime");
    if (datetime == NULL) return NULL;
    PyObject* datetime_date = PyObject_GetAttrString(datetime, "date");
    PyObject* result = NULL;
    if (datetime_date != NULL) {
        /* long long my_year, my_month, my_day = ... ; */
        PyObject* args = Py_BuildValue("(LLL)", my_year, my_month, my_day);
        res = PyObject_Call(datetime_date, args, NULL);
        Py_XDECREF(args);
    }
    Py_XDECREF(datetime_date);
    Py_DECREF(datetime);
    return result;
}

我的问题:

我想避免为导入付出高昂的代价,因为函数运行频繁。以上是高性能代码吗?

Does the PyImport_ImportModule("datetime") reload the entire module from scratch every time, or is it cached?

标准行为是 to first check sys.modules to see if the module has already been imported 和 return 如果可能的话。只有导入不成功才重新加载

您显然可以自己测试,方法是将一些具有可见副作用的代码放入模块中并多次导入(例如打印语句)。

模块导入系统 可自定义的,所以我相信另一个模块可以选择修改该行为(例如, pyximport 模块有一个选项总是重新加载)。因此,不能 100% 保证。

它可能仍然值得缓存,因为进行查找会产生一些成本 - 这是不必自己缓存它的便利性和速度之间的平衡。

When is the earliest time it is safe to try importing the other module?

Python解释器初始化后就安全了。如果您要将 Python 嵌入到 C/C++ 程序中,这是您需要考虑的事情。如果您正在编写 Python 扩展模块,那么您可以确信解释器已针对要导入的模块进行了初始化。

Can I assign it to a global variable, for example?

是的。但是,全局变量使您的模块难以支持干净地卸载和重新加载。许多 C 扩展选择不担心这个。但是,PyModule_GetState 机制旨在支持此用例,因此您可以选择将缓存置于扩展模块状态。