如何将装饰器应用于Cython cpdef函数 [英] How to apply decorators to Cython cpdef functions
问题描述
我最近一直在使用Cython,在将装饰器应用于Cython函数时遇到了这个错误
Cdef函数/类不能使用任意装饰器
这是我在修改的代码:
导入功能工具
def memoize(f):
计算= {}
@ functools.wraps(f)
def memoized_f(main_arg,* args,** kwargs):
(如果已计算).get(main_arg):
返回已计算[main_arg]
已计算[main_arg] = f(main_arg,* args,* * kwargs)计算出的
返回值[main_arg]
返回memoized_f
@memoize
cpdef int fib(int n):
如果n <1,则返回1 2 else fib(n-1)+ fib(n -2)
该错误表明cdef函数只能使用某些装饰器。
编辑:对于未来的读者:
p> @DavidW的答案种工作中提到的 g = plus_one(_g)
技巧。它不适用于递归。例如在我的示例代码中,执行 fib = memoize(fib)
不会记住对fib的递归调用,尽管它确实会记住顶级调用。例如,调用 fib(5)
会记住 fib(5)
调用的结果,但是会不是记住递归调用(即 fib(4),fib(3),fib(2),fib(1)
)
@DavidW指出, cdef,cpdef
函数在编译时已完全确定;装饰是运行时的事情,不会更新实际功能。
否-您可以很难为 cdef
函数编写装饰器。装饰器 cdef
函数采用的是类似 cython.boundscheck
这样的东西,它控制Cython代码的生成而不是用户生成的函数。 / p>
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):
#一些实现。 ..
第一个问题是g转换为C代码,例如 int g(double x,double y)
可以由函数指针表示,但不能像 plus_one
所期望的那样作为任意Python调用对象来表示。其次, wrapper
无法(通过C函数指针)知道 g
的参数是什么(可以t做 ** kwargs
)或任何简单的 * args
扩展方式。
您可以通过采用特定的函数指针类型并返回可调用的Python来制作类似于装饰器的东西:
cdef plus_one(int(* f)(double,double):
def wrapper(double x,double y):
return f(x,y)+ 1
返回包装器
cdef int _g(double x,double y):
#一些实现
g = plus_one(_g)#有点像装饰器
但是,您已经失去了使用 cdef
函数,因为 g
现在是可调用的通用Python,它具有所有开销。
< hr>
附录:一种替代的表达方式是装饰器是运行时Python功能(通常在mod上运行) ule import)。 cdef
函数是编译时的C功能。虽然可能不会实现编译时装饰器之类的东西,但对Cython来说将是一个相当重大的改变。
I've been playing around with Cython lately and I came across this error when applying a decorator to a Cython function
Cdef functions/classes cannot take arbitrary decorators
Here is the code I was tinkering with:
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)
The error suggests that cdef functions can only take certain decorators. Is it possible to write your own decorators that you can apply to cdef functions?
EDIT: For future readers:
The g = plus_one(_g)
trick mentioned in @DavidW's answer sort of works. It does not work with recursion. e.g. doing fib = memoize(fib)
in my example code does not memoize the recursive calls to fib, although it does memoize the top-level call. i.e. calling fib(5)
will memoize the result of the fib(5)
call, but it will not memoize the recursive calls (i.e. fib(4), fib(3), fib(2), fib(1)
)
As @DavidW points out, cdef, cpdef
functions are fully determined at compile time; the decoration is a runtime thing and does not update the actual function.
No - you can't easily write decorators for cdef
functions. The decorators cdef
functions take are things like cython.boundscheck
which control the Cython code generation rather than user generated functions.
The main difference between a cdef
function and a def
function is that a cdef
function has a C interface while a def
function becomes a Python callable so can be used from Python (but calling it is slightly less efficient because the arguments have to be passed in terms of PyObjects). [The inside of both a cdef
and def
function is compiled by Python so the only performance difference comes from the calling overhead]
The usual use of a decorator is to take an arbitrary Python callable and make some modification to it. For example
def plus_one(f):
def wrapper(*args,**kwargs):
return f(*args,**kwargs) + 1
return wrapper
now try to use it on a cdef function
cdef int g(double x, double y):
# some implementation...
The first problem is that g is translated to C code like int g(double x, double y)
which can be represented by a function pointer but not as an arbitrary Python callable like plus_one
expects. Second, wrapper
has no way of knowing (from a C function pointer) what g
s arguments are called (can't do **kwargs
) or any easy way of doing the *args
expansion.
You can make something like a decorator by taking a specific function pointer type and returning a Python callable:
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
However, you've lost the whole benefit of using a cdef
function since g
is now a generic Python callable with all the overhead that goes with it.
Addendum: an alternative way of putting it is that decorators are a runtime Python feature (usually run at module import). cdef
functions are a compile-time C feature. While it probably wouldn't be impossible to implement something like "compile-time decorators" it would be a pretty significant change to Cython.
这篇关于如何将装饰器应用于Cython cpdef函数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!