如何使用可导入的子模块创建 python C++ 扩展

Bla*_*Kow 5 c++ python cpython python-extensions

我正在为 python 创建一个 C++ 扩展。parent它创建一个包含子模块的模块child。有child一个方法hello()。如果我把它称为

import parent
parent.child.hello()
> 'Hi, World!'
Run Code Online (Sandbox Code Playgroud)

如果我尝试导入我的函数,它会失败

import parent
from parent.child import hello
> Traceback (most recent call last):
> File "<stdin>", line 1, in <module>
> ModuleNotFoundError: No module named 'parent.child'; 'parent' is not a package

parent.child
> <module 'child'>

Run Code Online (Sandbox Code Playgroud)

这是我的代码 setup.py

from setuptools import Extension, setup
  
# Define the extension module
extension_mod = Extension('parent',
                          sources=['custom.cc'])

# Define the setup parameters
setup(name='parent',
      version='1.0',
      description='A C++ extension module for Python.',
      ext_modules=[extension_mod],
      )

Run Code Online (Sandbox Code Playgroud)

和我的custom.cc

#include <Python.h>
#include <string>

std::string hello() {
    return "Hi, World!";
}

static PyObject* hello_world(PyObject* self, PyObject* args) {
    return PyUnicode_FromString(hello().c_str());
}

static PyMethodDef ParentMethods[] = {
    {nullptr, nullptr, 0, nullptr}
};

static PyMethodDef ChildMethods[] = {
    {"hello", hello_world, METH_NOARGS, ""},
    {nullptr, nullptr, 0, nullptr}
};

static PyModuleDef ChildModule = {
    PyModuleDef_HEAD_INIT,
    "child",
    "A submodule of the parent module.",
    -1,
    ChildMethods,
    nullptr,
    nullptr,
    nullptr,
    nullptr

};

static PyModuleDef ParentModule = {
    PyModuleDef_HEAD_INIT,
    "parent",
    "A C++ extension module for Python.",
    -1,
    ParentMethods,
    nullptr,
    nullptr,
    nullptr,
    nullptr
};

PyMODINIT_FUNC PyInit_parent(void) {
    PyObject* parent_module = PyModule_Create(&ParentModule);
    if (!parent_module) {
        return nullptr;
    }
    PyObject* child_module = PyModule_Create(&ChildModule);
    if (!child_module) {
        Py_DECREF(parent_module);
        return nullptr;
    }

    PyModule_AddObject(parent_module, "child", child_module);

    return parent_module;
}

Run Code Online (Sandbox Code Playgroud)

我安装并构建了python setup.py build install.

那么,如何确保我的包裹parent是包裹呢?

我的代码是一个玩具示例,但我实际上希望在 C++ 级别定义两个模块。我不想将它们分成几个模块 - 因为它们共享一些 C++ 代码。

我希望有类似于此答案的方法Python扩展与多个模块

Dav*_*ing 1

从扩展内部执行此操作是模拟导入系统识别为包的模块的行为的“简单”问题。(根据上下文,提供一个从外部执行相同操作的导入钩子可能会更好。)只需要进行一些更改:

  1. 给孩子起个名字"parent.child"
  2. 制作parent一个
    PyModule_AddObject(parent_module, "__path__", PyList_New(0));
    PyModule_AddStringConstant(parent_module, "__package__", "parent");
    
    Run Code Online (Sandbox Code Playgroud)
  3. 成为该child的成员:
    PyModule_AddStringConstant(child_module, "__package__", "parent");
    
    Run Code Online (Sandbox Code Playgroud)
  4. sys.modules如果执行了导入,则按照 Python 的方式 进行更新:
    PyDict_SetItemString(PyImport_GetModuleDict(), "parent.child", child_module);
    
    Run Code Online (Sandbox Code Playgroud)

当然,其中一些调用可能会失败;请注意,如果PyModule_AddObject失败,您必须删除对正在添加的对象的引用(为了清楚起见,我在这里没有这样做)。