我有这种 C/cython 项目:
project/
├── src/
│ └── modules/
│ ├── cython1.pyx
│ ├── cython1.pxd
│ ├── cython2.pyx
│ ├── cython2.pxd
│ ├── includes/
│ │ ├── c1.h
│ │ ├── c1.c
│ │ ├── c2.h
│ │ ├── c2.c
│ └── ...
└── setup.py
用这个
setup.py
:
filenames = ['cython1', 'cython2']
extensions = [Extension(name=f"modules.{name}",
sources=[f"modules/{name}.pyx"],
include_dirs=["modules/includes", ]) for name in filenames]
setup(
ext_modules=cythonize(extensions),
packages=['project'],
include_package_data=True,
package_data = {
'module': ['*.pxd', '*.so'],
},
]
)
我有几个问题。
在 pxd 文件中,我使用 C 文件中的定义,如下所示:
cdef extern from "includes/c1.c"
并且项目编译并且运行没有任何问题。
当我尝试
cimport module1
在另一个上下文中时,出现致命错误:找不到“c1.c”文件。
当我在 pxd 文件中设置
cdef extern from "includes/c1.h"
时,项目可以编译,但执行时会出现错误 (| ||:C 文件中的函数不在命名空间中)。
symbol not found in flat namespace
我尝试将相应的 C 文件添加到每个模块的源列表中 (
),然后收到一条消息,指出 C 函数已声明多次。事实上,我在不同的 cython 模块中使用 C 函数。而且,不同的c/h文件相互交互(例如“c2.c”中可以有一个
sources=[f"modules/{name}.pyx", "c1.c"]
)。
#include "c1.h"
最后,我无法管理它,也无法弄清楚了解如何构建项目以便其正常运行并且也可以导入。我从论坛了解到,一种解决方案可能是将我的 C 文件预编译为共享库,但我无法让此过程直接在
中工作。
setup.py
构建项目和编写的最佳方法是什么
?
setup.py
编辑:
我将项目的结构:
和setup.py更改为:
project/
├── src/
│ ├── modules_clib/
│ │ ├── c1.h
│ │ ├── c1.c
│ │ ├── c2.h
│ │ └── c2.c
│ │
│ └── modules/
│ ├── cython1.pyx
│ ├── cython1.pxd
│ ├── cython2.pyx
│ ├── cython2.pxd
│ └── includes.pxd
└── setup.py
所有
filenames = ['cython1', 'cython2']
extensions = [Extension(name=f"modules.{name}",
sources=[f"./src/modules/{name}.pyx"],
include_dirs=["./src/modules_clib", ]) for name in filenames]
setup(
ext_modules=cythonize(extensions),
packages=['project'],
include_package_data=True,
packages=['modules_clib', 'modules'],
package_dir={'': 'src'},
package_data={'modules_clib': ['*.c', '*.h'],
'modules': ['*.pxd', '*.so']},
]
)
现在都聚集在
cdef extern from "../modules_clib/c1.c"
文件中。在
includes.pxd
文件中,我
moduleX.pyx
来自
cimport
的所有必需的C函数。我现在也可以
modules.includes
或
cimport modules.includes
从另一个项目/笔记本使用C函数,因为
cimport modules.cythonX
被复制到
libc
目录中。我不知道
site-packages
直接复制到
libc
目录中是不是好事,但确实有效。
site-packages
但是,我还是被迫直接使用C文件。使用标头仍然会导致
据我了解,当使用 C 文件时,cython 直接使用使用它们的
symbol not found in flat namespace
文件来编译它们,如果使用标头则不是这样。
*.pyx
files that use them, which is not the case if headers are used.
好的,让我们来解决一下你关于在 Cython 项目中打包 C 库的问题。你碰到的问题在跨平台 C/Cython 集成中很常见,而且理解这些问题的解决方案对于创建良好的项目结构至关重要。
分析你的问题
-
“找不到文件”错误: 当你在一个 Cython 模块(
.pyx
) 中cimport
另一个模块时出现这个错误,表明 Cython 编译器找不到被cimport
模块所依赖的 C 头文件(.h
)。这是因为 Cython 需要这些头文件来理解 C 函数和类型的定义。 -
“符号未在平面命名空间中找到”错误: 这个错误表明,虽然 Cython 找到了头文件,但它在链接阶段无法解析对 C 函数的实际引用。这通常是因为 C 代码没有被正确编译成库,或者没有在链接过程中被正确链接。
推荐的解决方案和最佳实践
以下是一种构建 Cython 项目的结构化方法,该方法可以解决你遇到的问题,并提供更好的可维护性:
project/
├── src/
│ ├── modules_clib/ # 你的 C 库
│ │ ├── c1.h
│ │ ├── c1.c
│ │ ├── c2.h
│ │ └── c2.c
│ └── modules/ # 你的 Cython 模块
│ ├── cython1.pyx
│ ├── cython1.pxd
│ ├── cython2.pyx
│ ├── cython2.pxd
│ └── includes.pxd # 用于外部声明的中心位置
└── setup.py
setup.py
from setuptools import setup, Extension
from Cython.Build import cythonize
# 首先编译 C 库
clib_extension = Extension(
"modules_clib",
sources=[
"./src/modules_clib/c1.c",
"./src/modules_clib/c2.c",
],
include_dirs=["./src/modules_clib"], # 确保包含目录
)
# 然后编译 Cython 模块
cython_extensions = [
Extension(
"modules.cython1",
sources=["./src/modules/cython1.pyx"],
include_dirs=["./src/modules_clib"], # 用于 C 头文件
libraries=["modules_clib"], # 链接到 C 库
library_dirs=["./src"], # 库的路径
),
Extension(
"modules.cython2",
sources=["./src/modules/cython2.pyx"],
include_dirs=["./src/modules_clib"],
libraries=["modules_clib"],
library_dirs=["./src"],
),
]
setup(
name="your_project_name",
ext_modules=cythonize(cython_extensions) + [clib_extension], # 同时编译
packages=["modules"],
package_dir={"": "src"},
zip_safe=False, # 对于包含 .so 文件很重要
)
解释
-
分离的 C 库: 将 C 代码组织到一个单独的库(
modules_clib
)中,可以实现更清晰的代码结构和更容易的维护。 -
显式库编译和链接: 在
setup.py
中,首先使用Extension
为 C 代码创建一个扩展模块(clib_extension
)。这将在构建过程中编译你的 C 代码。然后,在你的 Cython 模块的Extension
中,使用libraries
和library_dirs
参数链接到这个编译好的 C 库。 -
包含目录: 确保
include_dirs
包含了 C 头文件的位置,以便 Cython 和 C 编译器都能找到它们。 -
zip_safe=False
: 为了让 Python 能够加载你的扩展模块(.so
文件),请在setup()
中设置zip_safe=False
。
includes.pxd
这个文件可以作为所有 C 函数和类型的中心声明点。
cdef extern from "modules_clib/c1.h":
# c1.h 中的函数和类型声明
cdef extern from "modules_clib/c2.h":
# c2.h 中的函数和类型声明
然后,你可以在你的 Cython 模块中使用
cimport modules.includes
来访问这些声明。
使用这个结构的好处
- 模块化: C 代码和 Cython 代码被清晰地分开。
- 可维护性: 更改 C 代码或 Cython 代码的影响被最小化。
- 可重用性: C 库可以很容易地在其他项目中重用。
通过遵循这些步骤,你可以建立一个更稳固、更可维护的 Cython 项目,该项目可以正确地与你的 C 代码集成。
标签:python,c,cython From: 78727843