编译不同包中依赖的pyx文件

11 投票
1 回答
2840 浏览
提问于 2025-04-16 13:03

我在不同的包中编译cdef类型时遇到了问题,但在Cython的文档中找不到解释。

我在我的Python源代码树的根目录下有一个setup.py文件:

from distutils.core import setup
from distutils.extension import Extension
from Cython.Distutils import build_ext

setup(
   cmdclass = {'build_ext': build_ext},
   ext_modules = [ 
      Extension("flink.pytk.defs.FragIdx", 
         sources = ["flink/pytk/defs/FragIdx.pyx"]),
      Extension("flink.pytk.fragments.STK_idx", 
         sources = ["flink/pytk/fragments/STK_idx.pyx"])
      ]   
)

FragIdx是一个cdef类型,定义在flink/pytk/defs/FragIdx.pyx文件中:

cdef class FragIdx:
   cdef public FragIdx parent
   cdef public FragIdx root
   cdef public tuple label
   ...

而STK_idx是FragIdx的扩展,定义在flink/pytk/fragments/STK_idx.pyx文件中:

from flink.pytk.defs.FragIdx import FragIdx
cdef class STK_idx(FragIdx):
   ...

当我尝试使用开头提到的setup.py进行编译时,FragIdx编译得很好,但在编译STK_idx时却出现了以下错误信息:

flink/pytk/fragments/STK_idx.pyx:5:5: 'FragIdx' is not a type name

请注意,我的源代码树的根目录已经在$PYTHONPATH中列出。

如果有人能对此提供一些帮助,我将非常感激,谢谢!

Daniele

1 个回答

12

哦,对于那些遇到类似问题的人,我好像找到了答案。

我原本以为 Python 会自动扫描编译到共享库 FragIdx.so 中的符号,但看起来这些信息必须通过一个 .pxd 文件明确提供(这个文件在运行 Cython 后会变成 C 的头文件)。

这个过程基本上分为两个步骤:

  1. 为父类创建一个定义文件(.pxd);
  2. 在子类模块中通过 cimport 导入父类定义(而不是用 import)。

为了更通俗易懂。

假设你在模块 pkg1.mod1 中定义了一个 cdef 类型 A。然后你在 pkg2.mod2 中定义了一个继承自 A 的类型 B

你的目录结构大概是这样的:

pkg1/
  mod1.pyx
  mod1.pxd
pkg2/
  mod2.pyx
  mod2.pxd

pkg1/mod1.pxd 中,你可能会有:

cdef class A:
  cdef int a
  cdef int b

而在 pkg1/mod1.pyx 中,你会提供你类的方法。

pkg2/mod2.pxd 中,你会有:

from pkg1.mod1 cimport A  #note "cimport"!!
cdef class B(A):
  cdef ... # your attributes here

同样,在 pkg2/mod2.pyx 中,你需要再次 cimport A 符号:

from pkg1.mod1 cimport A #note "cimport"!!
cdef class B(A):
  ... # your methods here

有趣的是,如果你只是想在 Python 代码中使用 A,而不是用它来定义一个子类型,那么定义文件 mod1.pxd 就不需要了。这是因为在创建扩展类型时,你需要让 C 编译器能找到这些定义,而在运行 Python 代码时就没有这个问题。不过,由于这点不是很直观,可能需要特别说明一下。

这些信息其实在 Cython 文档 中可以找到,虽然可能可以更明确一些。

希望这些信息能对某些人有所帮助。

撰写回答