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* :