如何使用Cython编写完整的Python包装器以包装C结构体?

9
我正在使用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`中。我在`py_context.pxd/pyx`中添加了`cdef c_context get(self)`。从`Bs __cinit__`调用`py_context.get()`会导致:`AttributeError: py_context object has no attribute get.`
看来我无法理解何时调用Cython中的`cdef`函数。
因此,我的问题是:从封装类中提取C结构体的最佳方法是什么?

你读过这个吗?http://docs.cython.org/src/tutorial/clibraries.html - chrisb
1个回答

6
问题在于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,因此可以调用Acdef函数。
本质上,您需要指定相关输入参数的类型到__cinit__函数中。

2
+1 这是一个更好的答案,因为我的回答有点误导,所以我立即将其删除。这里是一个网址,其中包含一个完整的示例,用于封装结构体,供 OP 查看是否需要。 - Dimitris Fasarakis Hilliard
非常好用。Bs __cinit__ 中缺少类型信息是问题的原因。添加 py_context 可以解决这个问题。 由于Cython文档有时不太明确,您的信息应该被添加到其中。 - a.Dippel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接