Cython:从python调用的cdef函数调用扩展类型cdef方法

时间:2016-10-14 20:54:09

标签: linux cython

我正在尝试编写一个Cython模块来计算成对距离,作为更大类别的局部敏感哈希的一部分。我没有为每种类型和每个距离度量编写代码,而是尝试创建一个cdef函数,该函数采用从Metric继承的各种扩展类型:

cdef class Metric:
    def __init__(self):
        pass

cdef class Euclidean(Metric):
    cdef numeric c_evaluate(self, numeric[:] x, numeric[:] y, int dims):
        ....

cdef numeric[:,:] pairwise(numeric[:] x, numeric[:] y, Metric func, bint symmetric):
    ...
    dm[i,j] = func.c_evaluate(x,y,dims)
    ...

要从Python访问此功能:

def py_pairwise(numeric[:,:] x, numeric[:,:] y, str func, bint symmetric = 1, **kwargs):
    cdef Metric mfunc = to_Metric(func, **kwargs)
    return pairwise(x, y, mfunc, symmetric)

但是我不断收到“c_distance。[Metric]对象没有属性'c_evaluate'”的错误。我想知道c_evaluate方法是否无法访问,因为类对象是通过python函数to_Metric在python代码中创建的,尽管我认为def和cdef函数应该能够在Cython模块中自由地相互调用。如果我将c_evaluate更改为cpdef方法,该方法有效,但我不确定这是否通过允许cdef对象通过python传递给cython或仅使用较慢的python方法来解决问题。任何建议(我也不在我的家用电脑上,所以我现在没有所有的代码。稍后会根据要求更新吗?)

编辑:错字不在原始函数中(可能还有其他函数):

ctypedef fused floating:
    float
    double

cdef class Euclidean(Metric):
    cdef public floating c_evaluate(self, floating[:] x, floating[:] y, int dims):
        cdef int i
        cdef floating tmp, d = 0
        for i in range(dims):
            tmp = x[i]-y[i]
            d += tmp*tmp
        return sqrt(d)

#@cython.boundscheck(False)
#@cython.wraparound(False)
def py_pairwise(numeric[:,::1] x, numeric[:,::1] y,str metric, bint symmetric,**kwargs):
    cdef Metric func = to_Metric(metric,**kwargs)   
    return pairwise(x,y,func,symmetric)

cdef numeric[:,::1] pairwise(numeric[:,::1] x,numeric[:,::1] y, Metric met, bint symmetric):#
    cdef int n,m,k,i,j
    n = x.shape[0]
    m = y.shape[0]
    dims = x.shape[1]
    if numeric in floating:
        mdtype = np.float
    else:
        mdtype = np.int
    #mdtype = np.float
    cdef numeric[:,::1] dm = (np.empty((n,m),dtype = mdtype)).fill(0)
    if symmetric:
        interval = lambda i,n,m: range(i+1,m)
    else:
        interval = lambda i,n,m: range(m)
    for i in range(n):
        for j in interval(i,n,m):
            dm[i,j] = met.c_evaluate(x[i,:],y[j,:],dims)
    return np.asarray(dm)

另外,to_Metric:

def to_Metric(str m, **kwargs):
    if len(kwargs) == 0:
        if m == 'euclidean':
            met = Euclidean()
        elif m in {'cos','cosine'}:
            met = Cosine()
        elif m in {'hamming','matching'}:
            met = Hamming()
        else:
            raise ValueError('Unrecognized metric {}'.format('\''+m+'\''))
    else:
        if m in {'pnorm','p-norm'}:
            met = Pnorm(kwargs['p'])
        elif m == 'maximal':
            met = Maximal(kwargs['m1'],kwargs['m2'],kwargs['sep'])
        else:
            raise ValueError('Unrecognized metric {}'.format('\''+m+'\''))
    return met

1 个答案:

答案 0 :(得分:0)

问题是c_evaluate与类Euclidean相关联,因此 可以与已知类型为{{1}的对象一起使用}。但是,在Euclidean中,您将pairwise的类型声明为met

因为您将Metric函数声明为c_evaluate,所以只能在编译时找到它。如果您希望在运行时找到cdef,就像标准Python函数一样,您应该将其声明为c_evaluate

如果您需要在编译时找到该函数(这使得调用它更快),那么您应该使def成为c_evaluate对象的函数,或者您应该{{1}只占用Metric个对象。