我最近一直在玩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函数的装饰器?
编辑:对于未来的读者:
@ DavidW回答类中提到的g = plus_one(_g)
技巧有效。它不适用于递归。例如在我的示例代码中执行fib = memoize(fib)
不会记住对fib的递归调用,尽管它会记住顶级调用。即,呼叫fib(5)
会记住fib(5)
来电的结果,但不会记住递归电话(即fib(4), fib(3), fib(2), fib(1)
)
正如@DavidW指出的那样,cdef, cpdef
函数在编译时完全确定;装饰是一个运行时的东西,不会更新实际的功能。
答案 0 :(得分:6)
否 - 您无法轻松为cdef
函数编写装饰器。装饰器cdef
函数采用cython.boundscheck
之类的东西来控制Cython代码生成而不是用户生成的函数。
cdef
函数和def
函数之间的主要区别在于cdef
函数具有C接口,而def
函数成为Python可调用函数,因此可以从Python中使用(但调用它的效率稍低,因为参数必须根据PyObjects传递)。 [ 一个cdef
和def
函数的内部由Python编译,因此唯一的性能差异来自调用开销]
装饰器的通常用法是采用任意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被转换为像int g(double x, double y)
这样的C代码,它可以用函数指针表示,但不能像plus_one
期望的那样任意Python可调用。其次,wrapper
无法知道(来自C函数指针)调用g
个参数(不能做**kwargs
)或任何简单的方法{ {1}}扩张。
你可以通过采用特定的函数指针类型并返回Python可调用来创建类似装饰器的东西:
*args
然而,你已经失去了使用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
现在是一个通用的Python可调用的,它带有所有开销
附录:另一种方法是装饰器是运行时Python功能(通常在模块导入时运行)。 g
函数是编译时C函数。虽然可能不可能实现类似&#34;编译时装饰器&#34;这对Cython来说是一个非常重要的变化。