【问题标题】:How to nest c-api extensions in a Python module and use them in that module?如何在 Python 模块中嵌套 c-api 扩展并在该模块中使用它们?
【发布时间】:2017-09-15 22:29:45
【问题描述】:

我想创建一个名为“mypack”的 Python 3 包,可以使用 pip 进行安装。它有一些 c 扩展和一些调用这些扩展的 python 代码。这是我的目录结构:

setup.py
mypack/__init__.py
mypack/mypack.py
foopkg/foo.cpp

setup.py 文件代码如下:

from setuptools import setup, Extension

PACKAGE_NAME = 'mypack'

module = Extension('foo',
                language = "c++",
                sources = ['foopkg/foo.cpp'])

setup(name=PACKAGE_NAME,
      version='1.0',
      packages=[PACKAGE_NAME],
      ext_package=PACKAGE_NAME,
      ext_modules=[module],
      include_package_data=True)

我从另一个相关问题中改编了这段代码,在该问题中,用户想要像我一样使用mypack.xxx 之类的东西来导入扩展。在 c-api 扩展中,我已经成功编译它并使它作为一个独立的扩展模块工作,但是我无法将它合并到一个更大的包中。它定义了两个函数make_arrayprint_array。为简洁起见,我删除了函数代码,只包含了 Python 需要的东西:

...

static PyMethodDef FooMethods[] = {
    { "make_array", make_array, METH_VARARGS, "Put number in array"},
    { "print_array", print_array, METH_VARARGS, "Print number from array"},
    { NULL, NULL, 0, NULL}
};

static struct PyModuleDef foomodule = {
    PyModuleDef_HEAD_INIT,
    "foo",
    "Make/Print array",
    -1,
    FooMethods
};

PyMODINIT_FUNC PyInit_foo(void)
{
    return PyModule_Create(&foomodule);
}

我希望能够在包中导入这个扩展来使用它(这是mypack.py):

import mypack.make_array
import mypack.print_array

def dostuff():
    array = make_array(10)
    print_array(array)

最后,我的__init__.py 文件包含from .mypack import dostuff

但是,当我使用 pip 安装并尝试运行导入 mypack 的测试脚本时,无论我使用 foo.xx 还是 mypack.xx,它都会抱怨 mypack.py 中的导入。我已经构建了其他具有嵌套结构的包,这些包使用模块中其他 python 文件中的代码,使用 __init__.py 并使用导入。但是我对如何使用 c-extensions 做到这一点有点困惑。

【问题讨论】:

    标签: python setuptools python-c-api python-c-extension


    【解决方案1】:

    模块是从 C 编译的这一事实对您编写 import 语句的方式没有影响。线...

    import mypack.make_array
    

    尝试导入在名为“mypack”的包中找到的名为“make_array”的模块。那不是你所拥有的。在名为“mypack”的包中有一个名为“foo”的模块,该模块中有两个对象(方法),名为“make_array”和“print_array”。

    如果您从__init__.py 中删除所有内容并使其以这种方式工作,则会简单得多。如果您想改进导入语句的语法,您可以稍后将内容添加到 init 文件中。你想要...

    from mypack.foo import make_array, print_array
    

    import mypack.foo as MYPACK
    

    在第一种情况下,方法现在位于全局命名空间中;在第二种情况下,您以MYPACK.make_arrayMYPACK.print_array 访问它们。

    我还发现将 C 扩展文件命名为 mypack 以及将 python 文件命名为 mypack.py 时存在问题。中只能有一个具有该名称的模块 一袋。一种解决方案是在 C 文件名前加上下划线。然后在 mypack.py 你写,例如...

    from ._mypack import make_array
    

    这会将make_array 放入myarray 的命名空间中,客户端代码不必知道或关心它是用C 编写的。

    【讨论】:

    • 感谢包和模块名称之间的澄清!我玩了一下你的建议。如果我错了,请纠正我,但似乎 c-api 代码创建了模块 foo,然后我将其归因于 setup.py 中的包 mypack。因此,当我在mypack.py 中执行from mypack.foo import make_array, print_array 时,它可以工作。但是,做from mypack.mypack import make_array, print_array 是行不通的。现在我了解了命名发生了什么,我应该能够在我的主代码中解决这个问题。您可能想要编辑您的答案,因此它使用from mypack.foo
    • 我会编辑我的答案,但我还不太明白。编译 foo.cpp 后,您将获得一个名为 foo.pyd 的文件。通常它与 foo.cpp 位于同一目录中。将其导入 python 程序时,其完整的导入名称取决于文件在目录结构中的位置。编译后是否将文件复制或移动到 mypack 目录?或者您的设置文件是否这样做?我不清楚您所说的“归属于包 mypack”是什么意思。据我所知,唯一可行的方法是物理文件位置和导入语句一致。
    • 好吧,是的,也许我弄错了。我在制作模块方面还很陌生。好的,这很有趣,所以我检查并 pip install 在mypack 而不是foopkg__pycache__ 目录下创建了.pyc__init__ 文件。 foopkg 是 cpp 文件所在的位置。
    • 共享库文件也在mypack下。
    • 我做过很多扩展模块,但从未编写过 pip 安装设置脚本。因此,不幸的是,我们在这里有点超出了我的专业知识。但也许您可以从这里开始,或者重新提出更有针对性的问题。
    猜你喜欢
    • 2010-12-13
    • 1970-01-01
    • 2011-03-31
    • 1970-01-01
    • 1970-01-01
    • 2014-02-21
    • 2018-07-20
    • 2018-02-26
    • 1970-01-01
    相关资源
    最近更新 更多