【问题标题】:Using function factories in pyx/pxd files to generate cython function wrappers for C-library在 pyx/pxd 文件中使用函数工厂为 C 库生成 cython 函数包装器
【发布时间】:2013-03-05 16:10:47
【问题描述】:

我正在重新评估将外部 C 库包装到 Python 中的不同方法。我很久以前就选择使用简单的 Python C API,它快速、简单、独立,而且,正如我所想的那样,它是面向未来的。然后我偶然发现了PyPy,它显然不打算支持 CPython API,但将来可能会成为一个有趣的替代方案……因此我正在寻找更高级别的入口点。 ctypes 很慢,所以现在我又回到了 cython,它似乎在努力支持 PyPy。

我的库中有许多具有相同签名的函数,因此我大量使用 C 预处理器宏来生成 Python 模块。我认为这在 cython 中会变得更舒服,因为我可以访问整个 Python 语言。但是,我在为我的函数包装器编写工厂时遇到了麻烦:

import cython
from numpy cimport ndarray, double_t

cimport my_c_library

cdef my_c_library.data D

ctypedef double_t DTYPE_t

cdef parse_args(ndarray[DTYPE_t] P, ndarray[DTYPE_t] x, ndarray[DTYPE_t] y):
    D.n = P.size
    D.m = x.size
    D.P = <double*> P.data
    D.x = <double*> x.data
    D.y = <double*> y.data

def _fun_factory(name):
    cpdef fun(ndarray[DTYPE_t] P, ndarray[DTYPE_t] x, ndarray[DTYPE_t] y):
        parse_args(P, x, y)
        getattr(my_c_library, name)(&D)
        return y
    return fun

fun1 = _fun_factory('fun1')
fun2 = _fun_factory('fun2')
# ... many more function definitions ...

cython 编译器报错:“C function definition not allowed here”,指的是_fun_factory 中的cpdef。这里有什么问题?我认为pyx 文件就像普通的 python 文件一样。除了从单独的 python 脚本(例如setup.py)动态生成pyx 文件之外,有没有办法让它工作?

我也很惊讶 cython 不让我这样做:

ctypedef ndarray[double_t, ndim=1] p_t

清理代码。为什么这不起作用?

我知道那里有自动的C -&gt; cython 翻译器,但我不愿意让自己依赖于这样的第 3 方工具。但是,如果您认为它已准备好用于生产,请随时提出建议。

【问题讨论】:

    标签: python numpy cython python-c-extension


    【解决方案1】:

    pyx 文件与 Python 文件不同,因为您可以匹配 C 和 Python 函数,并且对您可以使用 C(cdefcpdef)函数执行的操作有一些限制。一方面,您不能在运行时动态生成 C 代码,而这正是您的代码试图做的。由于fun 实际上只是在对其参数进行类型检查后执行一些 Python 代码,因此您不妨将其设为常规 Python 函数:

    def fun(P, x, y):
        parse_args(P, x, y)
        getattr(my_c_library, name)(&D)
        return y
    

    parse_args 将执行相同的参数检查,因此您不会丢失任何内容。 (不过,我不确定 getattr 是否适用于 cimport'd 的 C 库。您可能也想使用 import。)

    至于ctypedef,这可能是 Cython 中的一些限制/错误,目前还没有人能够修复。

    【讨论】:

    • 谢谢,所以你是说我从cython 获得的灵活性不如从C 预处理器获得的灵活性?这出乎意料...您如何看待将pyx 文件修补到setup.py 中的可能性?涉及很多大字符串,但 CPP 宏也不完全漂亮。
    • @Stefan:Cython 并不是将 C 语言转换为 Python 的灵丹妙药。相反,它将两者联系在一起并优化了一些 Python 代码。我不会在setup.py 中生成 Cython 源文件,那是维护的噩梦。我们在谈论多少个函数?
    • 我尝试了你的建议。正如您所怀疑的那样,getattr 不起作用(import my_c_library 没有帮助)。更令人惊讶的是,我什至不能将 my_c_library.fun1 作为第二个参数传递给工厂:“无法将 'data *' 转换为 Python 对象”。
    【解决方案2】:

    在玩了一些之后,以下似乎可行:

    def _fun_factory(fun_wrap):
        def fun(P, x, y):
            parse_args(P, x, y)
            fun_wrap()
            return y
        return fun
    
    def _fun1(): my_c_library.fun1(&D)
    def _fun2(): my_c_library.fun2(&D)
    # ... many more ...
    
    fun1 = _fun_factory(_fun1)
    fun2 = _fun_factory(_fun2)
    # ... many more...
    

    因此,似乎不可能对 my_c_library.fun1(&amp;D) 这样的表达式使用任何 Python 操作,显然需要按原样键入。只有在已经生成了第一组 Python 包装器时,才能在第二次传递中使用该工厂。这并不比显而易见的更优雅:

    cpdef fun1(ndarray[DTYPE_t] P, ndarray[DTYPE_t] x, ndarray[DTYPE_t] y):
        parse_args(P, x, y)
        my_c_function.fun1(&D)
        return y
    
    # ... many more ...
    

    在这里,cpdef 可以毫无问题地使用。所以我打算采用复制粘贴的方法......将来有人对 Cython 的预处理器宏感兴趣吗?

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-11-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-06-05
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多