我有一个用 C 编写的 python 模块,它有一个主模块和一个子模块(带点的名称,不确定这可以称为真正的子模块):
PyMODINIT_FUNC initsysipc(void) {
PyObject *module = Py_InitModule3("sysipc", ...);
...
init_sysipc_light();
}
static PyTypeObject FooType = { ... };
PyMODINIT_FUNC init_sysipc_light(void) {
PyObject *module = Py_InitModule3("sysipc.light", ...);
...
PyType_Ready(&FooType);
PyModule_AddObject(module, "FooType", &FooType);
}
该模块编译为sysipc.so
,当我把它放在当前目录中时,下面的导入工作没有问题:import sysipc
import sysipc.light
from sysipc.light import FooType
问题是我想把这个模块放在一个命名空间包中,文件夹结构是这样的:company/
company/__init__.py
company/dept/
company/dept/__init__.py
company/dept/sys/
company/dept/sys/__init__.py
company/dept/sys/sysipc.so
所有三个__init__.py
仅包含标准 setuptool
进口线:__path__ = __import__('pkgutil').extend_path(__path__, __name__)
在当前目录中,以下导入不起作用:from company.dept.sys import sysipc;
from company.dept.sys.sysipc.light import FooType;
我应该如何导入模块 sysipc.light
中定义的类型和方法在这种情况下?====================================
更新实际错误:
我有
sysipc.so
构建,如果我在当前目录中运行 python 作为这个模块,导入将按预期工作:[root@08649fea17ef 2]# python2
Python 2.7.18 (default, Jul 20 2020, 00:00:00)
[GCC 10.1.1 20200507 (Red Hat 10.1.1-1)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import sysipc
>>> import sysipc.light
>>>
但是,如果我将其放入命名空间文件夹中,如下所示:company/
company/__init__.py
company/dept
company/dept/__init__.py
company/dept/sys
company/dept/sys/sysipc.so
company/dept/sys/__init__.py
导入子模块将不起作用:>>> from company.dept.sys import sysipc
>>> from company.dept.sys import sysipc.light
File "<stdin>", line 1
from company.dept.sys import sysipc.light
^
SyntaxError: invalid syntax
>>> from company.dept.sys.sysipc import light
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ImportError: cannot import name light
>>>
该模块是用 this simple code 构建的,它适用于python2。我也有same example for python3 .
最佳答案
引自 https://www.python.org/dev/peps/pep-0489/#multiple-modules-in-one-library :
To support multiple Python modules in one shared library, the library can export additional PyInit* symbols besides the one that corresponds to the library's filename.
Note that this mechanism can currently only be used to load extra modules, but not to find them. (This is a limitation of the loader mechanism, which this PEP does not try to modify.) ...
换句话说,您需要为
importlib
重组项目如下能够找到子模块light
在 sysipc
包裹:company/__init__.py
company/dept/__init__.py
company/dept/sys/__init__.py
company/dept/sys/sysipc/__init__.py
company/dept/sys/sysipc/sysipc.so
company/dept/sys/sysipc/light.so -> sysipc.so # hardlink
light.so
之间的硬链接(hard link)和 sysipc.so
可以通过以下方式创建:ln company/dept/sys/sysipc/sysipc.so company/dept/sys/sysipc/light.so
然后在 company/dept/sys/sysipc/__init__.py
您从 sysipc.so
导入所有符号使用:from .sysipc import *
此外,您需要将子模块 C 扩展初始化函数的名称从 init_sysipc_light
更改为至init_light
对于 Python2,或来自 PyInit_sysipc_light
至PyInit_light
对于 Python3,因为 importlib
通过查找导出的 PyInit_<module name>
来加载模块来自动态模块,这里的模块名只有light
,即父包前缀不是(子)模块名称的一部分。这是扩展代码(Python3)和几个测试函数:
#include <Python.h>
PyObject *sysipc_light_foo(PyObject *self, PyObject *args) {
printf("[*] sysipc.light.foo\n");
return PyLong_FromLong(0);
}
static PyMethodDef sysipc_light_methods[] = {
{"foo", (PyCFunction)sysipc_light_foo, METH_VARARGS, "sysipc.light.foo function"},
{NULL, NULL, 0, NULL}
};
static struct PyModuleDef sysipc_light_module = {
PyModuleDef_HEAD_INIT,
"sysipc.light",
"sysipc child module",
-1,
sysipc_light_methods
};
PyMODINIT_FUNC PyInit_light(void)
{
PyObject *module = NULL;
module = PyModule_Create(&sysipc_light_module);
return module;
}
PyObject *sysipc_bar(PyObject *self, PyObject *args) {
printf("[*] sysipc.bar\n");
return PyLong_FromLong(0);
}
static PyMethodDef sysipc_methods[] = {
{"bar", (PyCFunction)sysipc_bar, METH_VARARGS, "sysipc.bar function"},
{NULL, NULL, 0, NULL}
};
static struct PyModuleDef sysipc_module = {
PyModuleDef_HEAD_INIT,
"sysipc",
"sysipc parent module",
-1,
sysipc_methods
};
PyMODINIT_FUNC PyInit_sysipc(void)
{
PyObject *module = NULL;
module = PyModule_Create(&sysipc_module);
PyInit_light();
return module;
}
测试.py:#!/usr/bin/env python3
from company.dept.sys import sysipc
from company.dept.sys.sysipc import light
sysipc.bar()
light.foo()
输出:[*] sysipc.bar
[*] sysipc.light.foo
关于如果放入命名空间包中,则子模块的python导入路径,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63978903/