Ste*_*hen 7 python cython cythonize
我最近一直在玩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)
Run Code Online (Sandbox Code Playgroud)
该错误表明cdef函数只能接受某些装饰器.是否可以编写自己的装饰器,可以应用于cdef函数?
编辑:为未来的读者:
将g = plus_one(_g)在@ DavidW的答复中提到绝招之类的作品.它不适用于递归.例如,fib = memoize(fib)在我的示例代码中,不会记住对fib的递归调用,尽管它会记住顶级调用.即调用fib(5)将memoize 调用的结果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传递) .[的内部既一个cdef和def功能是由编译的Python所以唯一的性能差异来自主叫开销]
通常使用装饰器是采用任意Python可调用并对其进行一些修改.例如
def plus_one(f):
def wrapper(*args,**kwargs):
return f(*args,**kwargs) + 1
return wrapper
Run Code Online (Sandbox Code Playgroud)
现在尝试在cdef函数上使用它
cdef int g(double x, double y):
# some implementation...
Run Code Online (Sandbox Code Playgroud)
第一个问题是g被转换为C代码int g(double x, double y),可以用函数指针表示,但不能像plus_one期望的那样任意Python可调用.其次,wrapper无法知道(从C函数指针)g调用什么参数(不能做**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
Run Code Online (Sandbox Code Playgroud)
但是,您已经失去了使用cdef函数的全部好处,因为g现在它是一个通用的Python可调用的,它具有所有开销.
附录:另一种方法是装饰器是运行时Python特性(通常在模块导入时运行).cdef函数是编译时C函数.虽然实现像"编译时装饰器"这样的东西可能并非不可能,但对于Cython来说这将是一个非常重大的改变.