Cython的类型

1 类型定义

1.1 定义一个C变量:

1.1.1 在Cython里定义一个C变量和C语言类似,不同的地方就是在声明的最前面要加上cdef,另外,末尾不用加分号";“如:

cdef int an[10]

cdef int n = 123

cdef int *pn = &n

printf("%d \n",pn[0])

1.1.2 这里要注意的是,以Cython里不能用类似*ptr这样代码来对指针变量进行取值,而必须用ptr[0]这样的形式,如上面的例子中,printf("%d\n",*pn)是编译通不过的

1.2 定义一个Python对象

1.2.1 和Python没有区别,直接给一个变量名赋值即可,如:

b = [1,2,3]

a = 'hello,world'

1.2.2 每个变量名实现上都是一个Python对象指针

2 类型转换

2.1 在Cython里用<>替代了()来进行类型转换,如:

cdef float a= 123.456

cdef int b

b = <int>a

2.2 再如:

cdef int n

a = 'hello,world'

n = <int>a

2.2.1 这里要注意的是:n = <int>a 把一个Python字符串对象指针强制转换成了一个整形变量,这在封装回调函数时经常会用到强制将一个Python对象指针强制转换成C类型的情况。

Cython的函数

1 函数定义:

1.1 在Cython里定义一个类C函数:

1.1.1 在这里之所以说是定义一个"类C”函数,而不是一个C函数,是因为它和纯C函数的定义还是有区别的,具体看下面的例子:

1.1.2 例1:

cdef int Max(int a,int b):

if a>b:

return a

else:

return b

可见,定义一个C函数和纯C还是比较相似的

但是语法却是遵循python的语法

1.1.3 再来看成一个比较怪异的例子,例2:

cdef Max(int a,b):

if a>b:

return a

else:

return b

这个例子就有点怪了,牛不是牛,马不像马,还是从翻译出来的c代码中去找答案吧:

先创建一个test.pyx

cdef Max(int a,b):

if a>b:

return a

else:

return b

再转成C代码:

cython -o test.c test.pyx

打开test.c,搜索cdef Max定义到下面的代码:

/* "test.pyx":1

cdef Max(int a,b):            #<<<<<<<<<<<<<<

*     if a>b:

*         return a

*/

static PyObject *__pyx_f_4test_Max(int __pyx_v_a, PyObject *__pyx_v_b) {

...

总结一下:

如果一个函数的参数或者返回值没有指定类型,那么它就是一个python对象,在进行操作时会将python对象转换成相应的C类型

如果参数或者返回值指定了类型,那么不会进行任何转换

1.1.4 再来看一个例子,例3

cdef object Max(int a,object b):

if a>b:

return a

else:

return b

和上一个例子不同的地方就是加了object关键,这表明这是一个python对象

它其实和上面生成的C代码是一样的,

这也验证了上面那个例子的理解是正确的

1.1.5 一个问题,如何在C代码里调用Cython里定义的C函数呢?

有时候,我们不仅仅是需要在Python代码里调用C代码,还有可能需要在Cython之外中的C代码里里调用Python的代码,

而我们知道,在Cython里定义的C函数是可以直接调用Python代码的

这样,可以通过在Cython外的C函数里调用Cython里定义的C函数来达到在C代码里调用Python代码的目的

但是,从上面的例子来看,经过Cython的翻译之后,函数名称被改变了,并且函数是static的,对外不可见,是不是无法调用?

答案很否定的,只要将Cython中定义的C函数声明为public或者extern即可,具体的来例子:

cdef public int Max(int a,int b):

if a>b:

return a

else:

return b

Cython翻译成C代码后:

cdef public int Max(int a,int b):            #<<<<<<<<<<<<<<

*     if a>b:

*         return a

*/

int Max(int __pyx_v_a,int __pyx_v_b) {

...

这样,这个函数就可以另外的C代码中补调用了。

但是,调用是可以调用,但是如果这个C函数里又调用了Python代码的话,在C代码里直接调用这个函数,程序会崩溃,这个另外的篇幅中再详细的讨论。

1.2  在Cython里定义一个类Python函数:

1.2.1 和类C函数的定义相似,只是以def开头,而不是以cdef开头

1.2.2 不同的地方是不能指定返回值的类型,这个很好理解,Python的函数肯定返回的是一个Python对象了,不可能返回一个C类型

1.2.3 即使你在Python里没有调用return,或者没有return一个对象,Python也默认会返回一个None对象

2 让类C函数在出错时抛出异常

2.1 这里有两种出错的情况

2.1.1 一种是C函数返回一个错误值

2.1.2 另外一种情况就是C函数里调用的Python代码出现的异常

2.2 首先来看一个例子:

cdef int ExceptTest(int n):

a = "abc"

print a.length()

return n

def CallExceptTest(intn):

return ExceptTest(n)

2.2.1 这里故意调用了一个字符串对象不存在的方法length

Subtopic

2.2.2 运行例子:

>>> import test

>>> test.CallExceptTest(-1)

Exception AttributeError: "'str'object has no attribute 'length'" in 'test.ExceptTest' ignored

0

2.2.3 可见错误被忽略了

2.3 如何让C函数抛出异常呢?还是先看例子:

import traceback

import sys

cdef int ExceptTest() except -1:

a = "abc"

print a.length()

return 1

def CallExceptTest():

try:

ExceptTest()

except Exception:

print "Exceptionin user code:"

traceback.print_exc(file=sys.stdout)

2.3.1 这个例子比上个例子在函数的声明时加上了 except -1

2.3.2 意思是异常产生时返回-1

2.3.3 测试一下:

>>> import test;test.CallExceptTest()

Exception in user code:

Traceback (most recent call last):

File "test.pyx", line 14,in test.CallExceptTest (test.c:809)

ExceptTest()

File "test.pyx", line 9,in test.ExceptTest (test.c:718)

print a.length()

AttributeError: 'str' object has noattribute 'length'

>>>

可见,这可比上个例子中只打印只一句冷冰冰的ignore好多了

通过异常时打出来的堆栈信息,可以定位到具体在test.pyx的哪一行出的错

这是不是比没有延时异常时好多了?

2.4 再来看下如果不是在C函数中调用的Python中出错会怎样:

import traceback

import sys

cdef int ExceptTest() except -1:

return -1

def CallExceptTest():

try:

ExceptTest()

except Exception:

print "Exceptionin user code:"

traceback.print_exc(file=sys.stdout)

2.4.1 这个例子中直接返了了-1

2.4.2 测试一下:

>>> importtest;test.CallExceptTest()

Traceback (most recent call last):

File "<stdin>", line1, in <module>

SystemError: errorreturn without exception set

2.4.3 直接抛出了一个SystemError,也没有堆栈信息输出

2.5 问题:有没有办法只要想在C函数中调用Python代码出错时抛出异常呢

2.5.1 答案当然是肯定的,在编程的世界里,永远都是只有想不到的,没有做不到的,

2.5.2 只要将except -1改成except? -1即可,

2.5.3 这样Cython就会在函数返回-1时调用PyErr_Occurred()来判断是否真的有一个异常产生,从而避免上个例子中出现的情况。

2.6 如果函数是void型,没有返回值,怎么让函数返回异常?

2.6.1 很简单,用except* 即可

如 cdef void Test() except*

Cython应用手记

作者: 日期:

gashero
2010-03-29

目录

  • 1   简介
  • 2   基本使用
  • 3   调用其他C库
    • 3.1   简单例子
    • 3.2   重新定义外部C库的定义
  • 4   类定义
  • 5   与Python交互

1   简 介

一种为Python写C扩展的方式,尝试一下。

参考文献:

  1. [r] 官方主页: http://www.cython.org/
  2. [r] Cython三分钟入门: http://blog.csdn.net/lanphaday/archive/2009/09/17/4561611.aspx
  3. [u] A quick Cython introduction: http://www.perrygeo.net/wordpress/?p=116 其实就是上文的原文
  4. [i] Cython's Documentation: http://docs.cython.org/ 看到"Extensioin types"

2   基 本使用

Cython基于pyrex,但是拥有更多功能和优化。用来写Python的C扩展的,并生成有效的C代码。写出的文件扩展名是 ".pyx" ,已经可以算作一种语言了。

一个简单的加法函数( addtest.pyx ):

def addtest(a,b):
cdef float c=a+b
return c

编译和生成动态库:

cython addtest.pyx
gcc -c -fPIC -I/usr/include/python2.5 addtest.c
gcc -shared addtest.o -o addtest.so

使用:

$ python
>>> import addtest
>>> addtest(1,2)
3.0

构建Cython代码的方式:

  1. 使用 setup.py ,常用
  2. 使用pyximport导入 ".pyx" 文件
  3. 运行cython命令编译出.c文件后再编译成.so文件
  4. 使用Sage

使用 setup.py 方式,例如一个 hello.pyx 文件,编写的 setup.py 如下:

from distutils.core import setup
from distutils.extension import Extension
from Cython.Distutils import build_ext ext_modules=[Extension('hello',['hello.pyx'])] setup(
name='Hello world app',
cmdclass={'build_ext':build_ext},
ext_modules=ext_modules
)

构建使用命令 python setup.py build_ext --inplace 

Cython提高速度的主要原因是使用静态类型。可以在任何参数前直接使用C的类型定义。函数内的话要加"cdef"前缀。如:

def f(double x):
cdef double ret
ret=x**2-x
return ret

仅仅使用Cython编译纯Python代码可以提高35%的性能,几乎全部使用静态类型以后提高4倍。

C风格函数声明,"except? -2"表示返回-2时就是出错了。不过"except *"是肯定安全的。如:

cdef double f(double) except? -2:
return x**2-x

使用cpdef时,这个函数就可以同时被C和Python调用了。当使用了C函数时,因为避开了昂贵的函数调用,旺旺可以提高150倍的速度。

不要过度优化,一步步的优化并且查看profile。使用"cython -a"参数可以查看HTML报告。

3   调 用其他C库

3.1   简 单例子

导入"math.h"中的 sin() 函数并使用:

cdef extern from "math.h":
double sin(double) cdef double f(double x):
return sin(x*x)

Cython不会去扫描头文件,所以自己必须再声明一遍。下面是使用时必须连接上其他库的 setup.py 文件:

from distutils.core import setup
from distutils.extension import Extension
from Cython.Distutils import build_ext ext_modules=[
Extension('demo',['demo.pyx',],libraries=['m',])
] setup(
name='Demos',
cmdclass={'build_ext':build_ext},
ext_modules=ext_modules,
)

同理可以使用任何动态或静态编译的库。

3.2   重 新定义外部C库的定义

一段C代码,头文件中的类型定义与函数声明:

typedef struct _Queue Queue;
typedef void *QueueValue; Queue *queue_new(void);
void queue_free(Queue *queue); int queue_push_head(Queue *queue, QueueValue data);
QueueValue queue_pop_head(Queue *queue);
QueueValue queue_peek_head(Queue *queue); int queue_push_tail(Queue *queue, QueueValue data);
QueueValue queue_pop_tail(Queue *queue);
QueueValue queue_peek_tail(Queue *queue); int queue_is_empty(Queue *queue);

对应的Cython定义,写入一个".pxd"文件中:

cdef extern from "libcalg/queue.h":
ctypedef struct Queue:
pass
ctypedef void* QueueValue Queue* new_queue()
void queue_free(Queue* queue) int queue_push_head(Queue* queue, QueueValue data)
QueueValue queue_pop_head(Queue* queue)
QueueValue queue_peek_head(Queue* queue) int queue_push_tail(Queue* queue, QueueValue data)
QueueValue queue_pop_tail(Queue* queue)
QueueValue queue_peek_tail(Queue* queue) bint queue_is_empty(Queue* queue)

大部分时候这种声明与头文件几乎是一样的,你可以直接拷贝过来。唯一的区别在最后一行,C函数的返回值其实是布尔值,所以用bint类型会转换成 Python的布尔值。

这里可以不关心结构体的内容,而只是用它的名字。

4   类 定义

一个类的例子:

cimport cqueue
cimport python_exc cdef class Queue:
cdef cqueue.Queue_c_queue
def __cinit__(self):
self._c_queue=cqueue.new_queue()

这里的构造函数是 __cinit__() 而不是 __init__() 。虽然 __init__() 依然有效,但是并不确保一定会运行(比如子类忘了调用基类的构造函数)。因为未初始化的指针经常导致Python挂掉而没有任何提示,所以 __cinit__() 总是会在初始化时调用。不过其被调用时,对象尚未构造完成,所以除了cdef字段以外,避免其他操作。如果要给__cinit__() 构造和函数加参数,必须与 __init__() 的匹配。

构造函数初始化资源时记得看看返回的资源是否是有效的。如果无效可以抛出错误。Cython提供了内存不足异常,如下:

def __cinit__(self):
self._c_queue=cqueue.new_queue()
if self._c_queue is NULL:
python_exc.PyErr_NoMemory()

Cython提供的析构函数,仅在建立成功内部对象时释放内部对象:

def __dealloc__(self):
if self._c_queue is not NULL:
cqueue.queue_free(self._c_queue)

将数据以通用指针方式进入,和返回时的强制类型转换:

cdef append(self,int value):
cqueue.queue_push_tail(self._c_queue,<void*>value) cdef int pop(self):
return <int>cqueue.queue_pop_head(self._c_queue)

Cython除了支持普通Python类以外,还支持扩展类型,使用"cdef class"定义。在内存占用和效率上更好。因为使用C结构体存储字段和方法,而不是Python字典。所以可以存储任意C字段类型,而不是其 Python包装。访问时也是直接访问C的值,而不是通过字典查找。

普通的Python类可以继承自cdef类,但是反之则不行。Cython需要知道完整的继承层次来定义C结构体,并且严格限制单继承。不过普通 Python类可以继承任一数量的Python类和扩展类型,无论在Python中还是在Cython代码中。

5   与 Python交互

如果Cython调用Python函数失败,则直接返回NULL,而不是异常对象。

如果一个函数既有可能返回NULL,也有可能返回0,则处理起来就比较麻烦。Python C API的做法是PyErr_Occurred() 函数。不过这种方式有性能损耗。在Cython中你可以自己指定哪个返回值代表错误,所以环境只要检查这个返回值即可。其他所有值都回无损耗的被接受。

在函数定义时指定except子句,则仅在函数返回该值时检查是否需要抛出异常。这样同一个函数返回0和返回0同时返回错误就可以区分开。例子:

cdef int pop(self) except? 0:
#...

类中的 cdef 定义C方法,而 cpdef 可以同时定义C方法和Python方法。

Cython 一篇通的更多相关文章

  1. JQuery 快速入门一篇通

    JQuery是什么? JQuery 是一套JavaScript库, 使用它,可以很方便的进行 JavaScript的编程.比如: 获取页面元素, 修改页面元素的CSS样式等等都可以以很简单的语法完成. ...

  2. sublime text (ST)一篇通(安装、配置、扩展、使用)

    sublime编辑器,功能插件多,可以扩展为IDE------------------------------------------- 1.安装 官网下载  http://www.sublimete ...

  3. Python全栈 MongoDB 数据库(Mongo、 正则基础、一篇通)

                  终端命令:       在线安装:         sudo apt-get install mongodb         默认安装路径 :  /var/lib/mong ...

  4. [Android]Volley源码分析(五)

    前面几篇通过源码分析了Volley是怎样进行请求调度及请求是如何被实际执行的,这篇最后来看下请求结果是如何交付给请求者的(一般是Android的UI主线程). 类图:

  5. TGL站长关于常见问题的回复

    问题地址: http://www.thegrouplet.com/thread-112923-1-1.html 问题: 网站配有太多的模板是否影响网站加载速度 月光答复: wp不需要删除其他的模板,不 ...

  6. XCL-Charts图表库中柱形图的同源风格切换介绍

    柱形图是被使用最多的图之中的一个,在写XCL-Charts这个Android图表库时,为它花费的时间相当多,不是由于有多难绘制,而是要在设计时怎样才干保证图基类能适应各种情况,能灵活满足足够多的需求, ...

  7. Spring Boot自动配置原理与实践(一)

    前言 Spring Boot众所周知是为了简化Spring的配置,省去XML的复杂化配置(虽然Spring官方推荐也使用Java配置)采用Java+Annotation方式配置.如下几个问题是我刚开始 ...

  8. CWMP开源代码研究番外篇——博通方案

    声明:本篇文章来自于某公司Cable Modem产品的文档资料,源码来自于博通公司,只提供参考(为保护产权,本人没有源码). 前文曾提到会写一篇关于博通的tr069,那么福利来了.福利,福利,福利,重 ...

  9. 24小时学通Linux内核总结篇(kconfig和Makefile & 讲不出再见)

    非常开心能够和大家一起分享这些,让我受益匪浅,感激之情也溢于言表,,code monkey的话少,没办法煽情了,,,,,,,冬天的风,吹得伤怀,倒叙往事,褪成空白~学校的人越来越少了,就像那年我们小年 ...

随机推荐

  1. .NET 中,编译器直接支持的数据类型称为基元类型(primitive type).基元类型和.NET框架类型(FCL)中的类型有直接的映射关系.

    .NET 中,编译器直接支持的数据类型称为基元类型(primitive type).基元类型和.NET框架类型(FCL)中的类型有直接的映射关系. The primitive types are Bo ...

  2. centos启动流程

    centos6启动流程 1.主板,post加电自检,检查硬件环境 2.主板选择一个硬盘进行引导,执行mbr446 grub stage1 3.grub stage1.5 加载/boot分区文件系统驱动 ...

  3. python元组的相对不可变性

    元组与多数python集合(列表.字典.集,等等)一样,保存的是对象的引用.如果引用的元素是可变的,即便元组本身不可变,但是元素依然可变.也就是说元组的不可变性其实是指tuple数据结构的物理内容(即 ...

  4. JS正则表达式学习总结

    JS正则:java RegExp对象,它是对字符串执行模式匹配的强大工具.运用最多的就是在输入处验证输入的字符串是否合法,指定用户输入字符串的格式. 定义方法: 1:直接量语法:var re=/pat ...

  5. PHP数组函数 array_multisort() ----对多个数组或多维数组进行排序

    PHP中array_multisort可以用来一次对多个数组进行排序,或者根据某一维或多维对多维数组进行排序. 关联(string)键名保持不变,但数字键名会被重新索引. 输入数组被当成一个表的列并以 ...

  6. Python 中的for,if-else和while语句

    for 循环 功能 for 循环是一种迭代循环机制,迭代即重复相同的逻辑操作,每次的操作都是基于上一次的结果而进行的.并且for循环可以遍历任何序列的项目,如一个列表或者一个字符串 语法 for 循环 ...

  7. graph-Kruskal-algorithm

    并查集是一种树型的数据结构,用于处理一些不相交集合(Disjoint Sets)的合并及查询问题.主要操作:1. 初始化:每个点所在集合初始化为其自身.2. 查找:查找元素所在的集合,即根节点.3. ...

  8. hdu4489 组合公式+dp

    这里对于题意在说明一下, 题目中要求的排列必须是波浪形,每一个在排列中的人不是波峰就是波谷,如果它既不是波峰也不是波谷排列就是错的. 对于我这种数学渣渣来说,做一道dp题要好久,%>_<% ...

  9. Numpy+Pandas读取数据

    1.为什么使用Numpy+Pandas 在使用Numpy读取csv文件时,文件中含有字符串时,会出现ValueError错误 2.Pandas读取csv文件:

  10. P1627 中位数

    P1627 中位数 题目描述 给出1~n的一个排列,统计该排列有多少个长度为奇数的连续子序列的中位数是b.中位数是指把所有元素从小到大排列后,位于中间的数. 输入输出格式 输入格式: 第一行为两个正整 ...