python 3.x C 扩展模块和子模块
Posted
技术标签:
【中文标题】python 3.x C 扩展模块和子模块【英文标题】:python 3.x C extension module and submodule 【发布时间】:2018-07-20 06:48:22 【问题描述】:当模块有子模块时,如何为 python 3.x 制作 C 扩展?例如,我有一个名为 pet.c 的文件:
#include <Python.h>
PyObject* CatMeow(PyObject* self)
return PyUnicode_FromString( ">*<" );
static PyMethodDef CatFunctions[] =
(char*) "meow", (PyCFunction) CatMeow, METH_NOARGS, NULL,
NULL, NULL, 0, NULL
;
static PyModuleDef CatDef =
PyModuleDef_HEAD_INIT, "cat", "cat ext", -1, CatFunctions,
NULL, NULL, NULL, NULL
;
PyMODINIT_FUNC PyInit_cat(void)
return PyModule_Create(&CatDef);
static PyModuleDef PetDef =
PyModuleDef_HEAD_INIT, "pet", "pet ext", -1, NULL,
NULL, NULL, NULL, NULL
;
PyMODINIT_FUNC PyInit_pet(void)
PyObject* p = PyModule_Create(&PetDef);
PyObject* c = PyInit_cat();
Py_INCREF(c);
PyModule_AddObject( p, "cat", c );
return p;
当我使用以下 setup.py 构建它时:
from distutils.core import setup, Extension
setup(
name='pet',
version='0.0',
ext_modules=[Extension('pet', ['pet.c'])]
)
我可以看到
>>> import pet
>>> pet.cat.meow()
'>*<'
或
>>> from pet import cat
>>> cat.meow()
'>*<'
这是预期的,但是当我尝试时
>>> from pet.cat import meow
我有一个 ModuleNotFoundError 说...没有名为“pet.cat”的模块; 'pet' 不是一个包,如果我尝试
>>> from pet import cat
>>> from cat import meow
我有一个 ModuleNotFoundError 说...没有名为“猫”的模块。但是如果我检查猫的类型
>>> type(cat)
<class 'module'>
说它是一个模块。
我该如何进行这项工作?将模块对象添加到另一个在 python 2.7 中运行良好的模块。由于绝对导入风格,它不应该在 python3 中工作吗?还是我必须使用 PEP 489 中描述的多阶段初始化?
【问题讨论】:
from pet.cat import meow
正在导入一个包,您没有包,请参阅 docs.python.org/3/tutorial/modules.html#packages 以使其工作,您需要一个名为 pet
的文件夹和名为 cat
的模块
@Simon,是的,我可以看到 pet 不是一个包裹。但是在 C 扩展中,创建文件夹是什么意思?您提供的链接解释了我何时使用普通的 python 文件而不是 C 扩展。
使你的字典结构如下:pet/cat.pyd
确保从扩展中删除宠物,然后from pet.cat import meow
将起作用
@Simon,我不明白......当我运行 python3 setup.py build-ext --inplace 时,它创建的只是一个共享对象(在 linux .so 文件中)。没有 pyd 文件和文件夹之类的东西。当我使用 pip3 install 安装它时,它会将共享对象复制到带有 egg 信息的站点包中。
您是否创建了__init__.py
文件?
【参考方案1】:
关于第一个抱怨宠物不是包裹的错误。如果 pet 只是为 cat 提供 parent,有一个简单的方法可以将它变成一个包:从 pet.c 中删除所有与 pet 相关的代码,并在 setup.py 中使用 ext_package
from distutils.core import setup, Extension
setup(
name = 'pet',
version = '0.0',
ext_package = 'pet',
ext_modules = [Extension('cat', ['pet.c'])]
)
运行上述程序将创建一个名为“pet”的目录和一个名称以“cat”开头的共享库。这有效地创建了一个命名空间包——有两种类型的包,常规和命名空间,后者是不需要 __init__.py 的包(详见 PEP 420)。从宠物外面,你可以做
>>> from pet import cat
>>> from pet.cat import meow
>>> meow()
'>*<'
>>> cat.meow()
'>*<'
之所以不能 from cat import meow 是因为模块的全称是 'pet.cat' 而不是 'cat',可以从 cat.__name__ 中确认。如果您在目录 pet 中运行解释器,则可以从 cat import meow 执行。
【讨论】:
感谢您的回答。如果宠物包含扩展对象怎么办?如果有多个子模块怎么办? ??您可以为每个子模块创建一个扩展,在这种情况下,您最终将在 pet 文件夹中拥有多个共享库。或者,您可以在一个共享库中导出多个 PyMODINIT_FUNC,并使用 PEP 489 中所述的多个符号链接。例如,如果您在 pet.c 中声明了 cat 和 dog,则可以使用与上述相同的 setup.py 创建 cat.xxx .so 在 pet 文件夹中,然后为它创建一个符号链接 dog.xxx.so。对于创建包,我不确定您是否可以在不创建实际路径的情况下设置 path。以上是关于python 3.x C 扩展模块和子模块的主要内容,如果未能解决你的问题,请参考以下文章