如何将装饰器应用于 Cython cpdef 函数
Posted
技术标签:
【中文标题】如何将装饰器应用于 Cython cpdef 函数【英文标题】:How to apply decorators to Cython cpdef functions 【发布时间】:2017-01-25 05:17:43 【问题描述】:我最近一直在玩 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
函数在编译时完全确定;装饰是运行时的东西,不会更新实际功能。
【问题讨论】:
简单评论一下我的技巧:请注意我使用了不同的名称。如果您将cdef
函数命名为_fib
,则使用fib = memorize(_fib)
并确保递归调用fib
(不是_fib
)我认为它会起作用,但是您会在每次调用时增加大量Python 开销.
【参考方案1】:
否 - 你不能轻易地为 cdef
函数编写装饰器。 cdef
函数采用的装饰器类似于 cython.boundscheck
之类的东西,它们控制 Cython 代码生成而不是用户生成的函数。
cdef
函数和 def
函数之间的主要区别在于 cdef
函数具有 C 接口,而 def
函数成为 Python 可调用函数,因此可以从 Python 中使用(但调用它是效率稍低,因为必须根据 PyObjects 传递参数)。 [both内部的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
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 来说将是一个非常重大的变化。
【讨论】:
知道了。谢谢你的解释。以上是关于如何将装饰器应用于 Cython cpdef 函数的主要内容,如果未能解决你的问题,请参考以下文章