如何使用 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 需要来自 Ac_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,因此您可以调用 As cdef 函数。

本质上,您需要为 __cinit__ 函数指定相关输入的类型。