我编写了一个最小的 C 函数(带有附带的头文件),目的是创建一个 Cython 包装器,该包装器可以从其他地方的 Python 文件中使用 C 代码。
我的文件是这样的:
C文件:
/* engine.c */
#include <stdio.h>
#include "customlib.h"
int engine(int value, int iterations) {
/* declare iteration index */
int idx;
for (idx = 0; idx < iterations; idx = idx + 1) {
value = value * 3;
}
/* return value that should be accessible by the Python module */
return value;
}
Run Code Online (Sandbox Code Playgroud)
C头文件:
/* customlib.h */
#ifndef CUSTOM_HEADER_H
#define CUSTOM_HEADER_H
/* engine function */
int engine(int value, int iterations);
#endif
Run Code Online (Sandbox Code Playgroud)
包装 C 代码的 Cython 模块:
# wrapper.pyx
cdef extern from "customlib.h":
int engine(int value, int iterations)
def call_c_code(value, iterations):
output_value = engine(value, iterations)
print(output_value)
Run Code Online (Sandbox Code Playgroud)
通过 Cython 包装器调用 C 代码的 Python 模块:
# caller.py
import wrapper
wrapper.call_c_code(1, 2) /* values not important; just an example */
Run Code Online (Sandbox Code Playgroud)
从 *.pyx 文件生成 *.so 的设置代码:
# setup.py
from distutils.core import setup
from distutils.extension import Extension
from Cython.Build import cythonize
# definitions
sourcefiles = ['engine.c']
extensions = [Extension('wrapper', sourcefiles)]
# setup function
setup(name='wrapper', ext_modules=cythonize('wrapper.pyx'))
Run Code Online (Sandbox Code Playgroud)
问题:共享对象 (*.so) 似乎编译没有任何问题。但是,即使只是导入也会wrapper.py引发以下错误:
Traceback (most recent call last):
File "caller.py", line 10, in <module>
import wrapper
ImportError: /home/alex/Documents/Projects/Cython/wrapper.cpython-36m-x86_64-linux-gnu.so: undefined symbol: engine
Run Code Online (Sandbox Code Playgroud)
在你的代码中setup.py,你实际上并没有做任何事情:
extensions = [Extension('wrapper', sourcefiles)]
Run Code Online (Sandbox Code Playgroud)
本质上,这只是死代码。它分配给一个变量,然后从不使用它。无论如何,您不想Extension从您的engine.c. Extension用于定义Python模块。这里唯一的 Python 模块是从wrapper.pyx.
相反,尝试类似的方法:
extensions = [Extension('wrapper', ['wrapper.pyx', 'engine.c'])]
setup(
...
ext_modules=cythonize(extensions)
...
)
Run Code Online (Sandbox Code Playgroud)
这还将编译engine.c其生成的目标文件并将其链接到您的扩展模块中。
帮助cythonize器函数足够智能,可以区分普通.c源和需要通过 Cython 编译器传递的 Cython 源。然后它将通过 C 编译器传递所有生成的 C 源代码并链接它们。