我们需要从Cython中的类的方法创建一个PyCapsule。我们设法编写了一个代码,可以编译甚至运行,但结果是错误的。在
这里有一个简单的例子:https://github.com/paugier/cython_capi/tree/master/using_cpython_pycapsule_class
胶囊由Pythran执行(需要使用github上的版本https://github.com/serge-sans-paille/pythran)。在
.pyx文件:
from cpython.pycapsule cimport PyCapsule_New
cdef int twice_func(int c):
return 2*c
cdef class Twice:
cdef public dict __pyx_capi__
def __init__(self):
self.__pyx_capi__ = self.get_capi()
cpdef get_capi(self):
return {
'twice_func': PyCapsule_New(
<void *>twice_func, 'int (int)', NULL),
'twice_cpdef': PyCapsule_New(
<void *>self.twice_cpdef, 'int (int)', NULL),
'twice_cdef': PyCapsule_New(
<void *>self.twice_cdef, 'int (int)', NULL),
'twice_static': PyCapsule_New(
<void *>self.twice_static, 'int (int)', NULL)}
cpdef int twice_cpdef(self, int c):
return 2*c
cdef int twice_cdef(self, int c):
return 2*c
@staticmethod
cdef int twice_static(int c):
return 2*c
pythran编译的文件(call_capsule_pythran.py
)。在
这又是Pythran的一个新特性,因此需要github上的版本。。。在
以及测试文件:
try:
import faulthandler
faulthandler.enable()
except ImportError:
pass
import unittest
from twice import Twice
from call_capsule_pythran import call_capsule
class TestAll(unittest.TestCase):
def setUp(self):
self.obj = Twice()
self.capi = self.obj.__pyx_capi__
def test_pythran(self):
value = 41
print('\n')
for name, capsule in self.capi.items():
print('capsule', name)
result = call_capsule(capsule, value)
if name.startswith('twice'):
if result != 2*value:
how = 'wrong'
else:
how = 'good'
print(how, f'result ({result})\n')
if __name__ == '__main__':
unittest.main()
它是一辆小车,它提供:
capsule twice_func
good result (82)
capsule twice_cpdef
wrong result (4006664390)
capsule twice_cdef
wrong result (4006664390)
capsule twice_static
good result (82)
结果表明,对于标准函数和静态函数,它都能很好地工作,但方法存在问题。在
注意,事实上,它对两个胶囊有效,似乎表明问题不是来自Pythran。在
在DavidW的评论之后,我明白我们必须在运行时(例如在get_capi
)创建一个C函数,该函数的签名int(int)
来自绑定方法twice_cdef
,其签名实际上是int(Twice, int)
。在
我不知道这是不是真的不可能与赛顿。。。在
跟进/扩展我的评论:
基本问题是pythoran希望PyCapsule中包含一个带有}。不幸的是,不可能使用纯C代码创建带有“绑定参数”的C函数指针,所以Cython不能(实际上也不能)这样做。在
int f(int)
签名的C函数指针。但是,方法的签名是int(PyObject* self, int c)
。2
被传递为self
(因为它实际上没有被使用,所以不会导致灾难…),并且使用一些任意的内存位来代替{修改1是通过创建一个接受正确类型的函数并在其中进行强制转换,而不是盲目地转换到
<void*>
中,从而更好地对传递给PyCapsules
的内容进行编译时类型检查。但在编译时不能解决问题:实际上,可以使用
ctypes
(或cffi
)从任意Python可调用项创建C函数—请参见Using function pointers to methods of classes without the gil(答案底部)。这增加了一层额外的Python调用,因此速度不是很快,代码也有点混乱。ctypes
通过使用运行时代码生成来实现这一点(这不是可移植的,也不是用纯C语言可以实现的)来动态构建一个函数,然后创建一个指向该函数的指针。在尽管您在评论中声称您不认为可以使用Python解释器,但我不认为这是真的—Pythran生成Python扩展模块(因此非常绑定到Python解释器),并且它似乎可以在下面显示的测试用例中工作:
^{pr2}$不幸的是,它只适用于}):
cpdef
,而不适用于cdef
函数,因为它依赖于Python的可调用性。cdef
函数可以与lambda一起使用(前提是将get_capi
改为def
,而不是{虽然有点乱,但可以让它工作。在
相关问题 更多 >
编程相关推荐