cython扩展类型的功能指针

时间:2019-04-30 10:16:36

标签: python cython

我正在编写一个cython模块,该模块提供了几种使用优化的cdef函数的扩展类型。这些扩展类型中的几种(大约10种,每种包含大约200行代码)具有完全相同的结构,但是没有调用相同的cdef函数。我想分解我的模块,以便只有一种扩展类型可以处理我需要的所有不同配置。

为了更加清楚,这是我正在编写的模块结构的一个(非常愚蠢的)示例:

cdef class A1:

    cdef double x

    def __init__(self, double x):
        self.x = x

    def apply(self):
        self.x = f1(self.x)

cdef class A2:

    cdef double x

    def __init__(self, double x):            
        self.x = x

    def apply(self):
        self.x = f2(self.x)       

cdef double f1(double x):
    return x**1

cdef double f2(double x):
    return x**2

...

以及我想要获得的分解代码的类型:

cdef class A:

    cdef int n
    cdef double x

    def __init__(self, double x, int n):
        self.x = x
        self.f = globals()[f'f{n}']

    def apply(self):
        self.x = self.f(self.x)

在纯Python中,使用globalsgetattr可以很容易地设置这种结构,但是在cython中,cdef对象是(当然)不能从python访问的,因此在globals()

我猜想,此类代码的C实现将使用函数指针,但我找不到在cython扩展类型内如何执行此操作。实际上,真正的问题是“是否可以将指向cdef函数的指针添加为实例属性(本身)?如果没有,如何在不损失性能的情况下分解这种代码(即不更改cdef函数)?

1 个答案:

答案 0 :(得分:2)

您可以使用函数指针,但是比纯python中的示例代码要多一些,例如:

%%cython

# here all possible functions double->double
cdef double fun1(double x):
    return 2*x

cdef double fun2(double x):
    return x*x;
...

# class A wraps functions of type double->double
ctypedef double(*f_type)(double) 

# boiler-plate code for obtaining the right functor
# uses NULL to signalize an error
cdef f_type functor_from_name(fun_name) except NULL:
    if fun_name == "double":
        return fun1
    elif fun_name == "square":
        return fun2
    else:
        raise Exception("unknown function name '{0}'".format(fun_name)) 


cdef class A:
    cdef f_type fun

    def __init__(self, fun_name ):
        self.fun = functor_from_name(fun_name)

    def apply(self, double x):
        return self.fun(x)

我不知道有可能在运行时从函数名称中获取cdef函数指针,而且我认为没有现成的方法。

现在它可以像宣传的那样工作:

>>> doubler = A("double")
>>> double.apply(3.0)
6.0
>>> squarer = A("square")
>>> squarer.apply(3.0)
9.0
>>> dummy = A("unknown")
Exception: unknown function name 'unknown'