动态链接Python扩展(.pyd)到另一个扩展

8 投票
2 回答
3242 浏览
提问于 2025-04-16 01:25

Python扩展模块其实就是动态库,所以我想把一个Python扩展动态链接到另一个扩展是有可能的。不过在Windows上,Python扩展的文件后缀是.pyd,而不是.dll,这让我在运行设置脚本时无法让distutils链接到它们。(我觉得在UNIX系统上就没这个问题,因为Python扩展用的是.so文件后缀。)

假设我有一个扩展bar.pyd,它需要链接到foo.pyd。在我的设置脚本中,我基本上做了以下操作:

from distutils.core import setup, Extension

foo = Extension("foo", sources=["foo.c"])
bar = Extension("bar", libraries=["foo"], sources=["bar.c"])
setup(ext_modules=[foo, bar])

到目前为止,这个方法不太奏效。这真的可行吗?我觉得应该可以,但我在网上找不到相关的信息。我在Windows上使用MinGW,但我希望这个方法也能在不同的MSVC++和其他系统上工作。

编辑:之前,我通过将foo编译时生成的目标文件(foo.o)传递给扩展的extra_objects选项来解决这个问题(这只有在我在bar中定义了所有foo符号的原型时才有效):

bar = Extension("bar", sources=["bar.c"], extra_objects=["build/.../foo.o"]

虽然这似乎不是正确的解决方案,但它确实有效。我对动态链接的理解不太深,所以这可能是正确的做法。不过感觉上有点不对劲。

接着,我尝试给gcc传递一些明确的参数,让它编译一个导入库:

foo = Extension("foo", sources=["foo.c"], extra_compile_args=["-Wl,--out-implib,foo.lib,--export-all-symbols"])

然后我把bar链接到新的导入库上:

bar = Extension("bar", libraries=["foo"], sources=["bar.c"])

这个编译没有报错,但有一些符号出现了问题(具体来说,我在foo中有几个全局的PyTypeObject,在bar中似乎被重新定义了。我需要这两个模块中的PyTypeObject指向同一个定义)。

编辑 2:所以,我找出了问题所在。在我构建并链接导入库后,函数符号正确导出了,但PyTypeObject却被重新声明了。假设在foo中有一个PyTypeObject Foo_Type。我在foo.h中声明了它,这个头文件被foo.cbar.c都包含了:

PyTypeObject Foo_Type;

我把这个声明去掉,然后在foo.c的顶部加上这个:

PyTypeObject __declspec(dllexport) Foo_Type;

bar.c的顶部加上这个:

PyTypeObject __declspec(dllimport) Foo_Type;

这样就解决了问题。我可以在foobar中使用Foo_Type,并且它们都指向同一个Foo_Type的定义。问题是,这种方法在非Windows系统上可能不行。我想如果我把__declspec去掉,其他系统上应该就能正常工作了。

2 个回答

-3
from distutils.core import Extension as cExtension
from pyd.support import setup, Extension

module1 = Extension("x", sources = ['xclass.c'])
module2 = Extension("y", sources = ['hello.d'], build_deimos=True)

setup(
    name = "x",
    version = '1.0',
    description = "eat a taco",
    ext_modules = [
        module1,
        module2
    ]
);

来自: http://pyd.readthedocs.io/en/latest/distutils.html

2

如果你在用普通的Python 导入机制,那么就不需要去链接其他的扩展。如果你想调用其他扩展里的函数,可能是因为你拿到了它的头文件,那么在链接之前,你需要先从DLL生成一个导入库。

撰写回答