本节介绍如何在一个Cython模块中使C语言的声明,函数和扩展类型可用于另一个Cython模块。这些设施都是基于Python导入机制的。并且可以被认为是它的编译时版本。
定义和实现文件
Cython模块可以分为两部分:具有.pxd后缀的定义文件,其中包含可用于其他Cython模块的C声明,以及.pyx后缀的实现文件,包含一切。当模块想要使用在另一个模块的定义文件中声明的东西时,它将使用cimport语句导入它。
仅包含extern声明的.pxd文件不需要与实际的.pyx文件或Python模块相对应。这可以使它成为一个方便的地方放置常见的声明,例如从几个模块中使用的外部库的函数的声明。
定义文件包含什么
一个pxd文件可能包含:
- 任何一种C类型声明。
- extern C函数或变量声明。
- 在模块中定义的C函数的声明。
- 扩展类型的定义部分(见下文)。
它不能包含任何C或Python函数,任何Python类定义或任何可执行语句的实现。当需要访问cdef属性和方法,或者继承自本模块中定义的cdef类时,需要这样做。
实现文件包含什么
一个实现文件可以包含任何类型的Cython语句,虽然对扩展类型的实现部分有一些限制,如果相应的定义文件也定义了该类型(见下文)。如果不需要从此模块中导入任何内容,那么这是唯一需要的文件。
cimport语句
cimport语句用于定义或实现文件,以访问在另一个定义文件中声明的名称。其语法与普通的Python导入语句完全相同:
cimport module [, module...] from module cimport name [as name] [, name [as name] ...]
这有一个例子。 dishes.pxd是导出C数据类型的定义文件。 restaurant.pyx是一个导入和使用它的实现文件。
dishes.pxd:
cdef enum otherstuff: sausage, eggs, lettuce cdef struct spamdish: int oz_of_spam otherstuff filler
restaurant.pyx:
cimport dishes from dishes cimport spamdish cdef void prepare(spamdish *d): d.oz_of_spam = 42 d.filler = dishes.sausage def serve(): cdef spamdish d prepare(&d) print "%d oz spam, filler no. %d" % (d.oz_of_spam, d.filler)
重要的是要了解,cimport语句只能用于导入C数据类型,C函数和变量以及扩展类型。它不能用于导入任何Python对象,并且(除了一个例外)它并不意味着在运行时任何Python导入。如果您想从引用的模块引用任何Python名称,那么还必须包含一个常规的import语句。
例外情况是,当您使用cimport导入扩展类型时,其类型对象将在运行时导入,并由导入它的名称提供。下面更详细地介绍使用cimport导入扩展类型。
如果.pxd文件更改,需要重新编译从其中导入的任何模块。
定义文件的搜索路径
当您导入名为modulename的模块时,Cython编译器会搜索名为modulename.pxd的文件。它沿路径搜索包含文件的文件(由-I命令行选项或cythonize()的include_path选项指定)以及sys.path。
使用package_data在setup.py脚本中安装.pxd文件允许其他软件包从模块中导入作为依赖项的项目。
另外,无论何时编译一个文件modulename.pyx,相应的定义文件modulename.pxd首先沿着include路径(但不是sys.path)进行搜索,如果找到,则在处理.pyx文件之前进行处理。
使用cimport解决命名冲突
cimport机制提供了一个干净简单的方法来解决使用相同名称的Python函数来封装外部C函数的问题。所有你需要做的是将extern C声明放入一个.pxd文件中的虚构模块,然后cimport该模块。然后,您可以通过使用模块的名称来限定它们来引用C函数。以下是一个例子:
c_lunch.pxd:
cdef extern from "lunch.h": void eject_tomato(float)
lunch.pyx:
cimport c_lunch def eject_tomato(float speed): c_lunch.eject_tomato(speed)
您不需要任何c_lunch.pyx文件,因为c_lunch.pxd中定义的唯一内容是extern C实体。运行时不会有任何实际的c_lunch模块,但这并不重要; c_lunch.pxd文件已经完成了在编译时提供额外命名空间的工作。
共享C函数
通过cimport可以在.pxd文件中放置头文件,可以在模块顶层定义的C函数,例如:
volume.pxd:
cdef float cube(float)
volume.pyx:
cdef float cube(float x): return x * x * x
spammery.pyx:
from volume cimport cube def menu(description, size): print description, ":", cube(size), \ "cubic metres of spam" menu("Entree", 1) menu("Main course", 3) menu("Dessert", 2)
注意:当模块以这种方式导出C函数时,会在函数名称下的模块字典中显示一个对象。但是,您不能从Python中使用此对象,也不能使用正常的import语句从Cython中使用它;你必须使用cimport。
共享扩展类型
扩展类型可以通过cimport将其定义分为两部分,一个在定义文件中,另一个在相应的实现文件中。
扩展类型的定义部分只能声明C属性和C方法,而不是Python方法,它必须声明所有该类型的C属性和C方法。
实现部分必须实现在定义部分中声明的所有C方法,并且不能添加任何进一步的C属性。它也可以定义Python方法。
以下是定义和导出扩展类型的模块的示例,以及使用它的另一个模块:
Shrubbing.pxd:
cdef class Shrubbery: cdef int width cdef int length
Shrubbing.pyx:
cdef class Shrubbery: def __cinit__(self, int w, int l): self.width = w self.length = l def standard_shrubbery(): return Shrubbery(3, 7)
Landscaping.pyx:
cimport Shrubbing import Shrubbing cdef Shrubbing.Shrubbery sh sh = Shrubbing.standard_shrubbery() print "Shrubbery size is %d x %d" % (sh.width, sh.length)
最后是编译脚本:
setup.py:
from distutils.core import setup from Cython.Build import cythonize setup(ext_modules = cythonize(["Landscaping.pyx", "Shrubbing.pyx"]))
这个例子可以知道:
- 在Shrubbing.pxd和Shrubbing.pyx中都有一个cdef类Shrubbery声明。当Shrubbing模块被编译时,这两个声明组合成一个。
- 在Landscaping.pyx中,cimport Shrubbing声明允许我们将Shrubbery类型称为Shrubbing.Shrubbery。但是在运行时它并不会在Landscaping的模块命名空间中绑定Shrubbing,所以要访问Shrubbing.standard_shrubbery(),我们还需要导入Shrubbing。
文章的脚注信息由WordPress的wp-posturl插件自动生成