问题描述
我有一个 cdef
函数返回一个 (int,int)
元组。我需要传播异常,因此必须为异常指定返回类型。由于我的函数从不返回负值,因此这可能例如(-1,-1)
。使用 documentation 中指定的标准语法,我的函数看起来像
cdef (int,int) myFunction(int arg) except (-1,-1):
...
但是,在对上述函数进行 cythonizing 时,出现错误
Not allowed in a constant expression
打开查找异常
cdef (int,int) myFunction(int arg) except *:
...
但这对我来说似乎效率低下。
如何为具有多个返回值的函数传播异常?
解决方法
答案是:您不能,至少不能使用 Cython 的 cdef 元组的当前版本。
潜在的问题是,对于此类元组,Cython 未定义运算符 ==
(这显然可以通过比较每个条目来完成 - 但在当前版本中未定义)。
对于一个简单的 cdef int myfun() except *
实际上并不是一个很大的性能损失:
- 如果结果不是
-1
,则几乎没有任何开销(仅与-1
进行比较)。 - 如果结果是
-1
,则使用PyErr_Occurred
来检查错误,这可能意味着相当大的开销(以上都是 nogil-blocks) - 可以通过
X
选择一个临界值except? X
,从而最小化/优化必要的PyErr_Occured
调用次数。
然而,如果 Cython 不知道类型的 ==
-operator(如 Cython 的 c-tuples),我们基本上处于 cdef void myfun() except *
情况,这意味着没有捷径和 {{ 1}} 必须始终检查/调用。
您可能会说 PyErr_Occured
是为 Cython 的元组定义的,因为它是经过编译的。但是如果您查看生成的代码,您会发现,为了进行比较,Cython 的 ctuples 被转换为 Python-tuples。
我个人首先会选择==
,看看它是否真的产生了可衡量的影响。因为 except *
-function 显然可以与 Python 交互,所以多加一点可能根本没有伤害。
如果是这样,最好的可能是更改函数的签名,例如
cdef
即使这感觉不那么流畅 - 由于更好的性能,它可能值得麻烦。