C 函数的调用
这个教程简要讲述了如何使用 Cython 调用 C 库函数。 如果想了解更多关于 C 库函数调用的内容,请参考C 函数的调用。
简单来说,我们先以一个 C 标准库中的函数为例。 你不需要向你的代码中引入 额外的依赖,Cython 都已经帮你定义好了这些函数。所以你可以将这些函数直接 cimport 进来并使用。
举个例子,比如说当你想用最简单的方法将char*
类型的值转化为一个整型值时,
你可以使用atoi()
函数,这个函数是在stdlib.h
头文件中定义的。我们可以这样来写:
from libc.stdlib cimport atoi
cdef parse_charptr_to_py_int(char* s):
assert s is not NULL, "byte string value is NULL"
return atoi(s) # note: atoi() has no error detection!
你可以在 Cython 的源代码包Cython/Includes/ <https://github.com/cython/cython/tree/master/Cython/Includes>
_.
中找到所有的标准 cimport 文件。这些文件保存在.pxd
文件中,这是一种标准再模块间共享 Cython 函数声明的方法(
见:ref:sharing-declarations
)。
Cython 也有一整套的 Cython 的C-API 函数声明集。 例如,为了测试你的 Cython 代码的 C 编译时间,你可以这样做:
from cpython.version cimport PY_VERSION_HEX
# Python version >= 3.2 final ?
print PY_VERSION_HEX >= 0x030200F0
Cython 也提供了 C math 库的声明:
from libc.math cimport sin
cdef double f(double x):
return sin(x*x)
动态链接(Dynamic linking)
在一些类 Unix 系统(例如 linux)中,默认不提供libc math 库。 所以除了
cimport函数声明外,你还必须配置你的编译器以链接共享库m
。 对于
distutils来说,在Extension()
安装变量libraries
中将其添加进来就可以了。
from distutils.core import setup
from distutils.extension import Extension
from Cython.Build import cythonize
ext_modules=[
Extension("demo",
sources=["demo.pyx"],
libraries=["m"] # Unix-like specific
)
]
setup(
name = "Demos",
ext_modules = cythonize(ext_modules)
)
外部声明(External declarations)
如果你想调用一个 Cython 中没有定义的函数声明,那么你必须自己进行声明。例如,上文中的 sin()
函数就是这样定义的:
cdef extern from "math.h":
double sin(double x)
此处声明了sin()
函数,这时我们便可在 Cython 代码中使用这个函数,并且让
Cython 生成一份包括math.h
头文件的 C 代码。C
编译器在编译时能够在math.h
中找到原始的函数声明。但是 Cython
不能解析math.h
并需要一个单独的定义。
正如math 库中的sin()
函数一样,只要 Cython
生成的模块正确的链接了共享库或静态库,我们就可以声明并调用任意的 C 库函数。
注意,只要简单地通过cpdef
声明,你就能从 Cython 模块中导出一个外部 C
函数。而且生成了一个 Python 扩展,使得我们可以在 Python 代码中直接访问到 C
函数sin()
:
>>> sin(0)
0.0
cdef extern from "math.h":
cpdef double sin(double x)
在属于 Cython 模块的.pxd
文件中(
一般与模块名一致,见:ref:sharing-declaration
)声明函数时,
你也可以达到同样的效果。 这使得其他 Cython 模块可以复用某个 C
声明。然而还是会在 Cython 模块中产生一份自动生成的 python 扩展。
变量的命名(Naming parameters)
C 和 Cython 都支持没有参数明的signature declarations:
cdef extern from "string.h":
char* strstr(const char*, const char*)
然而,这样的话 Cython 代码将不能通过关键字参数来调用这个函数(由Cython 0.19及以后的版本所支持)。所以,我们最好这样去声明一个函数:
cdef extern from "string.h":
char* strstr(const char *haystack, const char *needle)
这会让清楚地知道你所调用了哪两个参数,从而能够避免二义性并增强你的代码的可读性:
cdef char* data = "hfvcakdfagbcffvschvxcdfgccbcfhvgcsnfxjh"
pos = strstr(needle='akd', haystack=data)
print pos != NULL
注意,正如 Python 代码一样,对已有参数名的修改是不向后兼容的。那么, 如果你为外部的 C 或 C++ 函数进行了自己的声明,通常花一点时间去 将参数名命名的更好是非常值得的。