Skip to main content
 首页 » 编程设计

python之cython cdef C 类方法 : how to call it from another cython cdef class without python overhead

2025年05月04日11birdshome

我正在尝试在 cython 中实现通用排序算法。因此,我创建了以下模块,它在 sorter_t 类中实现了 Heapsort 算法:

# file general_sort_c.pyx 
 
from libc.stdint cimport int32_t 
cdef bint bint_true = 1 
cdef bint bint_false = 0 
 
cdef class sorter_t: 
 
    cdef object sortable_object 
 
    def __init__(self,sortable_object): 
        self.sortable_object = sortable_object 
 
    cpdef sort_c(self): 
 
        """ 
        https://en.wikipedia.org/wiki/Heapsort 
 
        """ 
 
        cdef int32_t end 
        cdef int32_t count = self.sortable_object.num_elements_int32 
 
        self.heapify_c(count) 
 
        end = count-1  
        while end > 0: 
            self.sortable_object.swap_c(0,end) 
            end = end - 1 
            self.siftDown_c(0,end) 
 
    cdef heapify_c(self,int32_t count): 
 
        cdef int32_t start = (count - 2)/2    
 
        while start >= 0: 
            self.siftDown_c(start, count-1) 
            start -= 1 
 
    cdef siftDown_c(self,int32_t start, int32_t end): 
 
        cdef int32_t root = start 
        cdef int32_t swap 
        cdef int32_t child 
 
        while root * 2 + 1 <= end: 
 
            child = root * 2 + 1  
            swap = root 
 
            # if "swap" < "child" then ... 
            if self.sortable_object.lt_c(swap,child) == 1: 
                swap = child 
 
            if child+1 <= end and self.sortable_object.lt_c(swap,child+1) == 1: 
                swap = child + 1 
 
            if swap != root: 
                self.sortable_object.swap_c(root,swap) 
                root = swap 
            else: 
                return 

当你定义一个 sorter_t 类型的对象时,你必须提供一个 sortable_object ,它有自己特定的 cdef 函数 lt_c 实现(对于比较一个元素是否小于另一个元素)和 swap_c(用于交换元素)。

例如,以下代码将从列表定义和创建一个sortable_object,并将使用该sortable_object 测试“sorter_t”的实现。

import numpy 
cimport numpy 
from libc.stdint cimport int32_t 
import general_sort_c 
 
cdef class sortable_t: 
 
    cdef public int32_t num_elements_int32 
    cdef int32_t [:] mv_lista 
 
    def __init__(self,int32_t [:] mv_lista): 
        self.num_elements_int32 = mv_lista.shape[0] 
        self.mv_lista = mv_lista 
 
    cdef public bint lt_c(self, int32_t left, int32_t right): 
        if self.mv_lista[left] < self.mv_lista[right]: 
            return 1 # True 
        else: 
            return 0 # False 
 
    cdef public bint gt_c(self, int32_t left, int32_t right): 
        if self.mv_lista[left] > self.mv_lista[right]: 
            return 1 # True 
        else: 
            return 0 # False 
 
    cdef public swap_c(self, int32_t left, int32_t right): 
        cdef int32_t tmp 
        tmp = self.mv_lista[right] 
        self.mv_lista[right] = self.mv_lista[left] 
        self.mv_lista[left] = tmp 
 
def probar(): 
 
    lista = numpy.array([3,4,1,7],dtype=numpy.int32) 
    cdef int32_t [:] mv_lista = lista 
 
    cdef sortable = sortable_t(mv_lista) 
    cdef sorter = general_sort_c.sorter_t(sortable) 
    sorter.sort_increasing_c() 
    print list(lista) 

编译完 .pyx 文件并在 IPython 控制台中运行以下命令后,出现以下错误:

In [1]: import test_general_sort_c as tgs 
 
In [2]: tgs.probar()       
 
... 
 
 general_sort_c.sorter_t.siftDown_increasing_c (general_sort_c.c:1452)() 
    132  
    133             #if mv_tnet_time[swap] < mv_tnet_time[child]: 
 
--> 134             if self.sortable_object.lt_c(swap,child) == bint_true: 
    135                 swap = child 
    136  
 
AttributeError: 'test_general_sort_c.sortable_t' object has no attribute 'lt_c' 

因此,问题是函数 lt_c 的实现从模块 general_sort_c.pyx 中的代码中看不到。如果我使用 cpdef 而不是 cdef 定义函数 lt_c 它会工作,但是你有很多 Python 开销。如何以 cdef(“纯 C”)方式调用此函数?

请您参考如下方法:

不幸的是,我不确定如何让它与融合类型一起工作,但剩下的很简单:

test_general_sort_c.pyx 需要免费的 test_general_sort_c.pxd:

from libc.stdint cimport int32_t 
 
cdef class sortable_t: 
    cdef public int32_t num_elements_int32 
    cdef int32_t [:] mv_lista 
    cdef public bint lt_c(self, int32_t left, int32_t right) 
    cdef public bint gt_c(self, int32_t left, int32_t right) 
    cdef public swap_c(self, int32_t left, int32_t right) 

general_sort_c.pyx 必须 cimport test_general_sort_c 并输入它的 self.sortable_object 成为一个 test_general_sort_c.sortable_t.

当然,如果您可以拥有多种受支持的类型,那就更好了。不过,我不确定你现在会怎么做。


此外,内置的 True 和 False 可以自己正常工作。

如果你更信任 Cython,你会发现你可以写

cdef public bint lt_c(self, int32_t left, int32_t right): 
    return self.mv_lista[left] < self.mv_lista[right] 
 
cdef public bint gt_c(self, int32_t left, int32_t right): 
    return self.mv_lista[left] > self.mv_lista[right] 
 
cdef public swap_c(self, int32_t left, int32_t right): 
    self.mv_lista[right], self.mv_lista[left] = self.mv_lista[left], self.mv_lista[right] 

就好了。 :)