如何将装饰器应用于 Cython cpdef 函数
How to apply decorators to Cython cpdef functions
我最近一直在玩 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
函数编写装饰器。装饰器 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
)或任何简单的方法来做 *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 可调用函数随之而来的所有开销。
附录:另一种表达方式是装饰器是 运行time Python 特性(通常是 运行 在模块导入时)。 cdef
函数是一个编译时 C 特性。虽然实现类似 "compile-time decorators" 的东西可能并非不可能,但这对 Cython 来说是一个非常重要的改变。
我最近一直在玩 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
函数编写装饰器。装饰器 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
)或任何简单的方法来做 *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 可调用函数随之而来的所有开销。
附录:另一种表达方式是装饰器是 运行time Python 特性(通常是 运行 在模块导入时)。 cdef
函数是一个编译时 C 特性。虽然实现类似 "compile-time decorators" 的东西可能并非不可能,但这对 Cython 来说是一个非常重要的改变。