如何使用 CFFI 在 C 中访问 Python dunder 方法?

How to access Python dunder methods in C using CFFI?

我试图按照 this example 尝试在 C 中创建一个简单的散列 table,我可以在 Python 中使用(使用 CFFI)。以下是相应的文件和相关代码。

hash_table.h:

typedef struct {
    size_t size;
    ...
    int cmp_function(const PyObject*, const PyObject*);
} hash_table_t;

build_hash_table.py:

from cffi import cffi

HEADER_FILE_NAME = "hash_table.h"
SOURCE_FILE_NAME = "hash_table.c"

ffi = FFI()

header = open(HEADER_FILE_NAME, "rt").read()
source = open(SOURCE_FILE_NAME, "rt").read()

ffi.set_source("hashtable.hash_table", header + source)
ffi.cdef(header)

if __name__ = "__main__":
    ffi.compile()

现在,到目前为止一切正常。但是,我将要在散列 table 中插入任何数据类型。如何访问我在 C 代码中收到的任何对象?例如,假设我在 Python:

中有这个 class
class Person():

    def __init__(self, name, id):
        self.name = name
        self.id = id

    def __eq__(self, other):
        return self.id == other.id

当我在散列 table 中搜索 Person 对象时,如何访问 __eq__ 方法?正如您在上面看到的,我在散列 table 中有一个声明为 int cmp_function(const void*, const void*); 的通用比较函数。目标是每次我搜索一个对象时,我都可以知道该对象的 __eq__ 方法定义 - 在 C 端。

希望我把问题说清楚了,先谢谢了!

参见 https://cffi.readthedocs.io/en/latest/using.html#extern-python-and-void-arguments。避免将 PyObject * 与 CFFI 一起使用。正确答案是保持int cmp_function(const void*, const void*);。我假设您已经使用 ffi.new_handle() 将对象转换为 void * 等价物,您可以将其发送到 C(在 add_to_hash_table() 方法等中)和 ffi.from_handle() 到读回它们(在 get_from_hash_table() 方法等中)。

要实现比较功能,请在Python 中用extern "Python" 声明一个函数,如上述文档中所示。然后,您将该函数用于结构中的 cmp_function 指针。在那个 Python 函数中,您收到两个参数作为两个 void *。您首先使用 ffi.from_handle() 将它们转换回原始 Python 对象,然后仅使用常规 Python---在您的情况下,可能只是 if x == y: return 1; else: return 0 或类似的对象。

调用 ffi.new_handle() 后要小心保持 Python 对象的活动。它们之所以能够存活,并不是因为它们的 void * 表示恰好存储在某些 C 结构中。

请注意,CFFI 不太适合实现纯数据结构,因为与直接 CPython-C-API 方法相比,所有这些转换和多次存储都会增加开销.

对于那些可能正在寻找另一个来比较 C 中的 Python 对象的人,我发现 this question 确切地解释了如何做到这一点。