【翻译】Cython教程4_Cython模块之间的共享

原创文章,转载请注明: 转载自勤奋的小青蛙
本文链接地址: 【翻译】Cython教程4_Cython模块之间的共享

本节介绍如何在一个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。
原创文章,转载请注明: 转载自勤奋的小青蛙
本文链接地址: 【翻译】Cython教程4_Cython模块之间的共享

文章的脚注信息由WordPress的wp-posturl插件自动生成



|2|left
打赏

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: