Cython基础教程(四) - 组织好cython代码

Organizing Cython code

Cython提供了三种类型的文件来组织Cython代码,最常用的是.pyx文件,也被叫做执行文件。如果Cython项目很小而且不需要额外的代码访问C-level的类或者结构体,那么一个.pyx文件就够了。但是如果需要C-level的类,那么可以将c-level的类定义在另外一个文件中。

假设现在有一个执行文件simulatior.pyx,包含一下内容(省略的函数体的定义)

  • A ctypedef
  • A cdef class named State to hold the simulation state
  • Two def functions, setup and output
  • Two cpdef functions

文件的大致结构如下

ctypedef double real_t

cdef class State:
    cdef:
        unsigned int n_particles
        real_t *x
        real_t *vx
    
    def __cinit__(...):
        #...
        
    def __dellloc__(...):
        #...
        
    cpdef real_t momentum(self):
        # ...

def setup(input_name):
    pass
    
cpdef run(State st):
    pass
    
cpdef int step(State st, real_t timestep):
    pass
    
def output(State st):
    pass

State扩展类型有__cinit__和__dealloc__方法用于分配和回收,还有一个cpdef定义的方法。因为所有的代码都在一个文件中,所有的函数都可以使用C-level的State类。如果该文件代码过长,为了方便阅读,可以将C-level的类的声明或者函数写在另一个**.pxd文件**中. 首先,新建一个文件simulator.pxd,在这个文件中声明C-level的类和函数。

ctypedef double real_t

cdef class State:
    cdef:
        unsigned int n_particles
        real_t *x
        real_t *vx
        
    cpdef real_t momentum(self):

    
cpdef run(State st)
    
cpdef int step(State st, real_t timestep)

注意,因为.pxd文件是定义文件(definition file), 只在编译的时候访问,所以我们只将C-level的声明写在.pxd文件中。Python-only的声明,例如用def定义的函数,是不能写入.pxd中的,否则会出现编译错误。我们的执行文件simulator.pyx同样需要改变,因为simulator.pxd和simulator.pyx有同样的名字,Cython认为它们有同样的命名空间,我们不能在simulator.pyx中进行重复声明,这样做会出现编译错误。现在我们的执行文件如下(simulator.pyx)

cdef class State:
    def __cinit__(...):
        #...
        
    def __dellloc__(...):
        #...
        
    cpdef real_t momentum(self):
        # ...

def setup(input_name):
    pass
    
cpdef run(State st):
    pass
    
cpdef int step(State st, real_t timestep):
    pass
    
def output(State st):
    pass

可以看出ctypedef和State的属性已经被移到了定义文件(simulator.pxd)中, 函数体的定义到保留在执行文件中。当编译simulator.pyx的时候,Cython编译器会自动检测simulator.pxd,使用它的声明,定义文件中可以包含的内容如下:

P105
定义文件中不能包含:
在这里插入图片描述

cimport语句

假设我们现在有另外一个版本的improved_simulator.pyx, 这个版本的simulator想使用我们之前定义的pyx中的setup和step函数,而且improved_simulator中会有一个子类继承simulator.pyx中的State类。improved_simulator.pyx文件如下:

from simulator cimport State, step, real_t
from simulator import setup as sim_setup

cdef class NewState(State):
    cdef:
        #...extra attribute
    def __cinit__(self, ...):
        pass
    def __dealloc__(self):
        pass
        
def setup(fname):
    pass
    
cpdef run(State st):
    pass

在improved_simulator.pyx的第一行,使用cimport语句来访问State扩展类,cpdef定义的step,以及ctypedef定义的real_t. 这个访问是C-level而且发生在编译的时候。cimport语句寻找simulator.pxd定义文件,而且只有声明之后才可以导入。import语句用来导入simulator扩展类型中def定义的函数,在程序运行的时候导入。
cimport语句和import语句有相同的语法。可以用cimport导入.pxd文件,像模块与样使用, 例如:

cimport simulator

# ...
cdef simulator.State st = simulator.State(params)
cdef simulator.real_t dt = 0.01
simulator.step(st, dt)

也可以提供模块的别名

cimport simulator as sim

# ...
cdef sim.State st = sim.State(params)
cdef sim.real_t dt = 0.01
sim.step(st, dt)

注意,当使用cimport导入python-level对象的时候会出现编译错误。相反,当使用import导入C-Only声明的时候也会出现编译错误。对于cpdef定义的函数或者扩展类型,可以使用import或者cimport(recommened)。

定义文件中可以包含extern blocks. 例如可以在twister.pxd中声明一些函数

cdef extern from "mt19937.h":
    void init_genrand(unsigned long s)
    
    unsigned long genrand_int32()
    
    ...

在.pyx执行文件中,可以用cimport导入需要用到的函数

from twister cimport init_genrand

Predifined Definition Files

Cython有很多预先定义好的定义文件(.pxd), 这些i那个一文件都被打包好放在Cython源文件夹下的includes目录中。包含c语言的标准库libc(有stdlib, stddio, math, string, stdint等.pyx文件),C++的标准哭libcpp(stringm vector, list, map, set, pair)。基本用法如下:

# using cimport with a module in a package
from libc cimport math
math.sin(3.14)

# using cimport with an object from a dotted module name
from libc.math cimport sin
sin(3.14)

# multiple named cimports
from libc.stdlib cimport rand, srand, qsort, malloc ,free
cdef int *a = <int*>malloc(10 * sizeof(int))

# using cimport with an alias
from libc.string cimport memcpy as c_memcpy

# using cimport with c++ stl template class
from libcpp.vector cimport vector
cdef vector[int] *vi = new vector[int](10)

如果使用cimport和import导入不同的函数但是有相同的名字,Cython会出现编译错误. 可以用别名进行区分

# compile time error
from libc.math cimport sin
from math import sin

定义文件(.pxd)和c/c++的头文件很相似:

  • They both declare C-level constructs for use by external code
  • They both allow us to break up what would be one large file into several components
  • They both declare the public C-level interface for implementation
  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值