我正在编写一个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中,使用globals
或getattr
可以很容易地设置这种结构,但是在cython中,cdef
对象是(当然)不能从python访问的,因此在globals()
。
我猜想,此类代码的C
实现将使用函数指针,但我找不到在cython扩展类型内如何执行此操作。实际上,真正的问题是“是否可以将指向cdef函数的指针添加为实例属性(本身)?如果没有,如何在不损失性能的情况下分解这种代码(即不更改cdef函数)?
答案 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'