这篇教程简短介绍了如何在Cython代码中调用C的库函数。如果需要一个更长且综合性更强的教程来指导你如何使用外部的C库、包装他们和处理错误,请看使用C库。
让我们从一个标准C库的函数开始。这不会给你的代码添加其他的依赖关系,而且Cython已经定义了很多这样的函数,所以你可以直接cimport并且使用它们。
举个例子,假设你需要用一种低层方法来从char*值中解析数字,你可以使用被stdlib.h头文件定义的atoi()函数,如下所示:
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/里找到标准cimport文件的完整列表。它们被存储在.pxd文件中。.pxd文件提供了一种可以在模块间共用可重利用的Cython声明的标准方法。(请看在Cython模块间共用声明)。
Cython还有一个供CPython的C-API使用的声明的完备集。举例来说,要在C编译时测试你的代码是由哪个CPython版本编译的,你可以进行如下操作:
from cpython.version cimport PY_VERSION_HEX
# Python version >= 3.2 final ?
print(PY_VERSION_HEX >= 0x030200F0)Cython还提供了给C数学库使用的声明:
from libc.math cimport sin
cdef double f(double x):
return sin(x * x)libc math库在部分类Unix系统(比如Linux)中没有被默认链接,因此比较特殊。除了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))如果你想访问一些C代码,而这些C代码还未被Cython提供准备好使用的声明,你就必须自己声明它们。比如说,上面的sin()就被定义如下:
cdef extern from "math.h":
double sin(double x)这将以Cython代码可以使用的一种方式声明sin()函数,并命令Cython生成包含math.h头文件的C代码。C编译器在编译时将会在math.h里找到原始的声明,但是Cython并不会解析"math.h",并且需要一个单独的定义。
正如math库中的sin()函数一样,只要Cython生成的模块被正确地链接到共享库或静态库中,我们就可以声明或是调用任何C库。
注意你可以可以通过将Cython模块声明为cpdef轻松地导出一个外部C函数。这会为它生成一个Python包装器,并把它加入模块字典中。下面是一个为Python代码提供了直接访问C的sin()函数功能的Cython模块:
"""
>>> sin(0)
0.0
"""
cdef extern from "math.h":
cpdef double sin(double x)当这个声明出现在属于Cython模块的.pxd文件中时,你将得到相同的结果(即具有相同名称的文件,请看在Cython模块间共用声明)。这允许在其他Cython模块中重用C声明,同时仍然在此特定模块中提供自动生成的Python包装器。
C和Cython都支持没有参数名的签名声明,如下所示:
cdef extern from "string.h":
char* strstr(const char*, const char*)但是,这会阻碍Cython通过关键词参数调用它。所以,我们最好使用如下的声明替代:
cdef extern from "string.h":
char* strstr(const char *haystack, const char *needle)现在,你就可以搞清楚在你的调用中,这两个参数分别是什么,这样就可以避免歧义,并使你的代码可读性更高:
cdef extern from "string.h":
char* strstr(const char *haystack, const char *needle)
cdef char* data = "hfvcakdfagbcffvschvxcdfgccbcfhvgcsnfxjh"
cdef char* pos = strstr(needle='akd', haystack=data)
print(pos is not NULL)注意,稍后更改现有参数名称是一种向后不兼容的API修改,就像对Python代码一样。因此,如果你为外部C或C++函数提供了自己的声明,那么通常值得花费额外的精力来选择它们的参数名。