Cython基础--Cython的函数

本文详细介绍了Cython中的函数定义,包括类C函数和类Python函数的差异,如何在C代码中调用Cython定义的C函数,以及在函数出错时如何抛出异常。通过示例展示了在Cython中处理异常的方法,帮助理解Cython与C、Python之间的交互。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

 


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值