Cython:返回类型化内存视图的函数的异常类型

Cython: exception type for a function returning a typed memoryview

在函数的 cdef 签名中:

cdef const unsigned char[:, :] my_fn(input) except <????> :

我应该在 <????> 中输入什么?

如果我对 documentation 的理解正确,则必须指定异常类型才能将异常向上传播到 Python 堆栈。

我尝试了 [b'\x00'] 和空 Cython 数组,none 有效。

坏消息:你做不到。好消息:您不必这样做!

只有当 cdef 函数 return 是一个 int、一个枚举、一个浮点数或一个指针时,except <xxx> 的语法才是可能的——基本上在 C.

中通过 == 进行比较是有意义的

类型化内存视图是一个 Python 对象,当 returned 对象是空指针时,它有一个内置的方式来发出错误信号。因此,您不必定义异常值,因为它已经被定义了!

例如:

%%cython
cdef int[:] worker(int[:] memview, int index):
    memview[index]=10 
    return memview

def runit(index):
    cdef int mem[4]
    print(worker(mem,index))

现在

runit(4)   #4 -> out of bounds
print("I still run")

不打印 "I still run" 因为越界异常被传播。

return 值不是 Python 对象,例如 int:

%%cython
cdef int worker(int[:] memview, int index):
    return memview[index]

现在:

runit(4)   #4 -> out of bounds
print("I still run")

打印“0”和 "I still run",因为错误没有传播。我们可以选择一个特殊值,例如 -1,这样错误就会通过 return-value=-1:

传播
%%cython
cdef int worker(int[:] memview, int index) except -1:
    return memview[index]

现在,不再打印"I still run"。

然而,有时并没有很好的异常值,例如因为 memview 可以包含任何整数值:

%%cython
cdef int worker(int[:] memview, int index) except -1:
    return memview[index]

def runit(index):
    cdef int mem[4]
    mem[0]=-1
    print(worker(mem, index))

现在,运行

runit(0)
print("I still run")

以虚假错误结束:

SystemError: returned NULL without setting an error

解决方法是使用

cdef int worker(int[:] memview, int index) except *

runit(0)runit(4) 具有正确的行为。

那么与 except -1 相比,使用 except * 的成本是多少?他们不是真的高:

如果 returned 值是 -1(这是默认的 "exceptional" 值),那么我们知道可能发生了错误(这只是一种可能性,而不是确定性)并通过 PyErr_Occurred() 检查是否真的如此。

正如@DavidW 在评论中提到的,也可以使用 except? -1 ,其优点是更易于阅读和理解。有趣的是,这会产生与 except * 相同的 C 代码,因为默认错误值是 -1!

然而,except?语法允许我们选择我们必须支付PyErr_Occurred()开销的函数结果。例如,如果我们知道结果 -1 经常出现而 -2 几乎从不出现,那么我们可以使用 except? -2 并且 PyErr_Occured() 仅在结果出现时才被检查函数的参数是 -2,这意味着几乎从不(在 except * 的情况下,它会经常被检查——每次 -1 被 returned)。