如何将装饰器应用于Cython cpdef函数
我最近一直在使用Cython,在将装饰器应用于Cython函数时遇到了此错误
Cdef functions/classes cannot take arbitrary decorators
这是我修改过的代码:
import functools
def memoize(f):
computed = {}
@functools.wraps(f)
def memoized_f(main_arg, *args, **kwargs):
if computed.get(main_arg):
return computed[main_arg]
computed[main_arg] = f(main_arg, *args, **kwargs)
return computed[main_arg]
return memoized_f
@memoize
cpdef int fib(int n):
return 1 if n < 2 else fib(n - 1) + fib(n - 2)
该错误表明cdef函数只能使用 某些 装饰器。是否可以编写自己的装饰器以应用于cdef函数?
编辑:对于未来的读者:
该g = plus_one(_g)
绝招@ DavidW的答复中提到 排序的 作品。它不适用于递归。例如,fib =
memoize(fib)
在我的示例代码中执行的操作不会记住对fib的递归调用,尽管它确实会记住顶级调用。也就是说,调用fib(5)
会记住调用的结果fib(5)
,但
不会 记住递归调用(即fib(4), fib(3), fib(2), fib(1)
)
正如@DavidW指出的那样,cdef, cpdef
函数是在编译时完全确定的。装饰是运行时的东西,不会更新实际功能。
-
不
,您无法轻松编写cdef
函数的装饰器。装饰器cdef
函数需要cython.boundscheck
控制Cython代码生成而不是用户生成的函数。cdef
函数与def
函数之间的主要区别在于,cdef
函数具有C接口,而def
函数变为可调用的Python,因此可以从Python使用(但调用它的效率稍低,因为必须根据PyObjects传递参数)
。[ a和函数的内部 都是 由Python编译的,因此唯一的性能差异来自调用开销]cdef``def
装饰器的通常用法是采用任意可调用的Python并对其进行一些修改。例如
def plus_one(f): def wrapper(*args,**kwargs): return f(*args,**kwargs) + 1 return wrapper
现在尝试在cdef函数上使用它
cdef int g(double x, double y): # some implementation...
第一个问题是g被转换为C代码,就像
int g(double x, double y)
可以通过函数指针表示的那样,但是却不能像plus_one
预期的那样被任意Python调用。其次,wrapper
没有办法(从C函数指针知道)什么g
叫s参数(不能做**kwargs
),也没有任何简单的方法来进行*args
扩展。您可以通过采用特定的函数指针类型并返回可调用的Python来制作类似装饰器的内容:
cdef plus_one(int (*f)(double, double): def wrapper(double x, double y): return f(x, y) + 1 return wrapper cdef int _g(double x, double y): # some implementation g = plus_one(_g) # kind of like a decorator
但是, 您已经失去了使用
cdef
函数的全部好处,因为g
现在它是一个可调用的通用Python,带有所有随之而来的开销。
附录:一种替代的表达方式是装饰器是运行时的Python功能(通常在模块导入时运行)。
cdef
函数是编译时的C功能。尽管可能并非不可能实现“编译时装饰器”之类的东西,但对Cython来说将是一个相当重大的改变。