如何使用 Cython 围绕 C 结构编写完整的 Python 包装器?
How to write a complete Python wrapper around a C Struct using Cython?
我正在使用 Cython 为 Python 的 C 库编写高级接口。
我有一个扩展类型 A
,它使用指向更复杂的 C 上下文结构 c_context
的指针来初始化库。指针保存在A
.
A
也有一个 def
函数,它又创建了另一个扩展类型 B
,用库函数调用初始化另一个 C 结构。在 B
.
中进行的后续库调用需要此结构
B
需要来自 A
的 c_context
指针,它被我包装在扩展类型 py_context
中,以便将它从 B
传递给 __cinit__
:
#lib.pxd (C library definitions)
cdef extern from "lib.h":
ctypedef struct c_context:
pass
#file py_context.pxd
from lib cimport c_context
cdef class py_context:
cdef c_context *context
cdef create(cls, c_context *context)
cdef c_context* get(self)
#file py_context.pyx
def class py_context:
@staticmethod
cdef create(cls, c_context *c):
cls = py_nfc_context()
cls.context = c
return cls
cdef c_context* get(self):
return self.context
使用正确的 C 上下文传递包装器效果很好。
现在我需要再次从 py_context
中取出 C 结构并将其保存在 B
中。我将 cdef c_context get(self)
添加到 py_context.pxd/pyx
。
从 Bs __cinit__
调用 py_context.get()
结果:AttributeError: py_context object has no attribute get.
似乎我不知道什么时候在 Cython 中调用 cdef
函数。
所以我的问题是: 再次从包装器 class 中提取 C 结构的最佳方法是什么?
问题在于 Cython 在编译时不知道 py_context
变量的数据类型。对 cdef
函数的调用在编译时解析,并且不存在通过属性查找在运行时找出它的机制(与正常的 Python 函数一样)。
[请注意,在 Cython 中编写的 def
函数仍然是编译的并且可以指定数据类型,因此如果它们具有正确的信息,则完全能够调用 cdef
函数。]
你没有给出出错的相关代码(类型B
的构造函数),但这里有一个非常简化的例子,希望能给你一些修复它的方法:
cdef class A:
cdef f(self):
return
def f1(var):
var.f()
#f1(A()) # will fail at runtime with an attribute error
在 f1
中,var
的类型未知,因此您无法调用 cdef
函数。
def f2(A var):
var.f()
f2(A()) # will work
f2(1) # will fail, int can't be converted to A
在f2
中,var
的类型被限制为A
,因此它可以愉快地调用与A
关联的cdef
函数。如果你传递给它的不是 A
的东西,你将在运行时得到一个 TypeError
。
def f3(var):
cdef A another_reference_to_var = var # this does test that the types match
another_reference_to_var.f()
f3(A()) # will work
f3(1) # will fail, int can't be converted to A
函数f3
可以接受任何类型的变量。但是,当您将它分配给 another_reference_to_var
时,它被 cdef
编辑为 A
它会检查类型是否匹配(如果不匹配则引发运行时异常)。由于 another_reference_to_var
在编译时已知为 A
,因此您可以调用 A
s cdef
函数。
本质上,您需要为 __cinit__
函数指定相关输入的类型。
我正在使用 Cython 为 Python 的 C 库编写高级接口。
我有一个扩展类型 A
,它使用指向更复杂的 C 上下文结构 c_context
的指针来初始化库。指针保存在A
.
A
也有一个 def
函数,它又创建了另一个扩展类型 B
,用库函数调用初始化另一个 C 结构。在 B
.
中进行的后续库调用需要此结构
B
需要来自 A
的 c_context
指针,它被我包装在扩展类型 py_context
中,以便将它从 B
传递给 __cinit__
:
#lib.pxd (C library definitions)
cdef extern from "lib.h":
ctypedef struct c_context:
pass
#file py_context.pxd
from lib cimport c_context
cdef class py_context:
cdef c_context *context
cdef create(cls, c_context *context)
cdef c_context* get(self)
#file py_context.pyx
def class py_context:
@staticmethod
cdef create(cls, c_context *c):
cls = py_nfc_context()
cls.context = c
return cls
cdef c_context* get(self):
return self.context
使用正确的 C 上下文传递包装器效果很好。
现在我需要再次从 py_context
中取出 C 结构并将其保存在 B
中。我将 cdef c_context get(self)
添加到 py_context.pxd/pyx
。
从 Bs __cinit__
调用 py_context.get()
结果:AttributeError: py_context object has no attribute get.
似乎我不知道什么时候在 Cython 中调用 cdef
函数。
所以我的问题是: 再次从包装器 class 中提取 C 结构的最佳方法是什么?
问题在于 Cython 在编译时不知道 py_context
变量的数据类型。对 cdef
函数的调用在编译时解析,并且不存在通过属性查找在运行时找出它的机制(与正常的 Python 函数一样)。
[请注意,在 Cython 中编写的 def
函数仍然是编译的并且可以指定数据类型,因此如果它们具有正确的信息,则完全能够调用 cdef
函数。]
你没有给出出错的相关代码(类型B
的构造函数),但这里有一个非常简化的例子,希望能给你一些修复它的方法:
cdef class A:
cdef f(self):
return
def f1(var):
var.f()
#f1(A()) # will fail at runtime with an attribute error
在 f1
中,var
的类型未知,因此您无法调用 cdef
函数。
def f2(A var):
var.f()
f2(A()) # will work
f2(1) # will fail, int can't be converted to A
在f2
中,var
的类型被限制为A
,因此它可以愉快地调用与A
关联的cdef
函数。如果你传递给它的不是 A
的东西,你将在运行时得到一个 TypeError
。
def f3(var):
cdef A another_reference_to_var = var # this does test that the types match
another_reference_to_var.f()
f3(A()) # will work
f3(1) # will fail, int can't be converted to A
函数f3
可以接受任何类型的变量。但是,当您将它分配给 another_reference_to_var
时,它被 cdef
编辑为 A
它会检查类型是否匹配(如果不匹配则引发运行时异常)。由于 another_reference_to_var
在编译时已知为 A
,因此您可以调用 A
s cdef
函数。
本质上,您需要为 __cinit__
函数指定相关输入的类型。