cython 扩展类型中的函数指针

Ips*_*ium 3 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

...
Run Code Online (Sandbox Code Playgroud)

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

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)
Run Code Online (Sandbox Code Playgroud)

在纯 Python 中,这种结构很容易使用globals或来设置getattr,但在 cython 中,cdef对象(当然)无法从 python 访问,因此在globals().

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

ead*_*ead 5

您可以使用函数指针,但比纯 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)
Run Code Online (Sandbox Code Playgroud)

我不知道在运行时从函数名称获取 -function 指针的可能性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'
Run Code Online (Sandbox Code Playgroud)