如何将装饰器应用于Cython cpdef函数 [英] How to apply decorators to Cython cpdef functions

查看:201
本文介绍了如何将装饰器应用于Cython cpdef函数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我最近一直在使用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 gs 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆