如何使用 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 确切地解释了如何做到这一点。
我试图按照 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:
中有这个 classclass 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 确切地解释了如何做到这一点。