Cython跨pyx文件cimport报ModuleNotFoundError问题咨询
问题成因
报错的核心原因是Cython扩展的模块声明路径、源码存放位置和实际导入路径不匹配:
- 你在
setup.py中声明扩展模块的全限定名是lst_viewer.extensions.instrument,也就是说编译完成后,这个模块属于lst_viewer.extensions包下的子模块,不存在顶层名为instrument的模块。 - 你在
read_events.pyx中写from instrument cimport ...时,Cython编译阶段会扫描当前工作目录找到根目录extensions/下的instrument.pxd,因此能正常通过编译;但运行时Python会按照模块全路径查找顶层instrument模块,自然会抛出ModuleNotFoundError。 - 你之前在根目录
extensions/下加__init__.pxd无效,是因为这个目录本身不在Python包搜索路径内,你把Cython源码(.pyx/.pxd)和实际发布的Python包(src/lst_viewer/)分开放置,本身就不符合Python模块的查找规则,安装后根本不会把根目录的.pxd文件打包进去。
修正方案
按以下步骤调整即可解决问题:
- 调整目录结构
把根目录extensions/下的所有Cython源文件(instrument.pxd、instrument.pyx、read_events.pyx)移动到src/lst_viewer/extensions/目录下,和已有的__init__.py同级,同时在该目录下新增空的__init__.pxd文件标记Cython包,调整后结构如下:
lst_viewer/ setup.py src/ lst_viewer/ extensions/ __init__.py __init__.pxd instrument.pxd instrument.pyx read_events.pyx
- 修正
setup.py配置
删掉重复导入的distutils.core相关语句,同时修改Extension的源文件路径,匹配新的源码位置:
import os from Cython.Distutils import build_ext as cython_build_ext from setuptools import find_packages, setup, Extension EXTENSIONS = [ Extension( 'lst_viewer.extensions.instrument', sources=[os.path.join('src', 'lst_viewer', 'extensions', 'instrument.pyx')] ), Extension( 'lst_viewer.extensions.read_events', sources=[os.path.join('src', 'lst_viewer', 'extensions', 'read_events.pyx')] ), ] CMDCLASS = {'build_ext': cython_build_ext} setup( name="lst_viewer", packages=find_packages('src'), include_package_data=True, package_dir={'': 'src'}, ext_modules=EXTENSIONS, cmdclass=CMDCLASS )
- 修正cimport导入路径
修改read_events.pyx中的导入语句,使用和模块声明一致的全限定路径:
from lst_viewer.extensions.instrument cimport Instrument, get_instrument def read_events(str instrument_name): cdef Instrument instr = get_instrument(instrument_name)
- 清理旧编译产物后重新编译
先删除之前生成的build/目录、所有自动生成的.c/.so编译缓存文件,再执行pip install -e .或者python setup.py build_ext --inplace重新编译安装即可。
注意:如果需要在同包的Cython模块间简化导入,也可以在
__init__.pxd中重导出需要的符号,但直接写全限定路径是最稳妥、不会出现路径查找问题的写法。
内容的提问来源于stack exchange,提问作者Eurydice
相关产品推荐
相关产品推荐

