简单的 Cython 文件在使用 cpdef 时导致重定义错误

Simple Cython file causes redefinition errors when using cpdef

我有一个简单的 cython *.pyx 文件导致了一堆重定义错误,我不明白为什么。如果我将 'cpdef' 更改为 'cdef' 它编译正常,但不会导出这些函数,我希望它们被导出。有人可以解释为什么会失败,或者我在这里做错了什么吗?

core.pyx:

cdef extern from "module.h":
    cpdef double radians(double degrees)
    cpdef double degrees(double radians)

module.h:

#ifndef MODULE_H
#define MODULE_H
double radians(double degrees);
double degrees(double radians);
#endif

module.c:

#include "module.h"

double radians(double degrees)
{
    return degrees * (M_PI / 180.0);
}

double degrees(double radians)
{
    return radians * (180.0 / M_PI);
}

错误:

$ python3 setup.py build
running build
running build_ext
building 'core' extension
x86_64-linux-gnu-gcc -pthread -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O2 -Wall -g -fstack-protector-strong -Wformat -Werror=format-security -g -fwrapv -O2 -g -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -fPIC -Iinclude -I/usr/include/python3.8 -c src/core.c -o build/temp.linux-x86_64-3.8/src/core.o
src/core.c:1294:13: error: redefinition of ‘__pyx_doc_11cfunc_dot_to_py_36__Pyx_CFunc_double____double___to_py_wrap’
 1294 | static char __pyx_doc_11cfunc_dot_to_py_36__Pyx_CFunc_double____double___to_py_wrap[] = "wrap(radians: float) -> float";
      |             ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/core.c:1146:13: note: previous definition of ‘__pyx_doc_11cfunc_dot_to_py_36__Pyx_CFunc_double____double___to_py_wrap’ was here
 1146 | static char __pyx_doc_11cfunc_dot_to_py_36__Pyx_CFunc_double____double___to_py_wrap[] = "wrap(degrees: float) -> float";
      |             ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/core.c:1295:20: error: redefinition of ‘__pyx_mdef_11cfunc_dot_to_py_36__Pyx_CFunc_double____double___to_py_1wrap’
 1295 | static PyMethodDef __pyx_mdef_11cfunc_dot_to_py_36__Pyx_CFunc_double____double___to_py_1wrap = {"wrap", (PyCFunction)__pyx_pw_11cfunc_dot_to_py_36__Pyx_CFunc_double____double___to_py_1wrap, METH_O, __pyx_doc_11cfunc_dot_to_py_36__Pyx_CFunc_double____double___to_py_wrap};
      |                    ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/core.c:1147:20: note: previous definition of ‘__pyx_mdef_11cfunc_dot_to_py_36__Pyx_CFunc_double____double___to_py_1wrap’ was here
 1147 | static PyMethodDef __pyx_mdef_11cfunc_dot_to_py_36__Pyx_CFunc_double____double___to_py_1wrap = {"wrap", (PyCFunction)__pyx_pw_11cfunc_dot_to_py_36__Pyx_CFunc_double____double___to_py_1wrap, METH_O, __pyx_doc_11cfunc_dot_to_py_36__Pyx_CFunc_double____double___to_py_wrap};
      |                    ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/core.c:1296:18: error: redefinition of ‘__pyx_pw_11cfunc_dot_to_py_36__Pyx_CFunc_double____double___to_py_1wrap’
 1296 | static PyObject *__pyx_pw_11cfunc_dot_to_py_36__Pyx_CFunc_double____double___to_py_1wrap(PyObject *__pyx_self, PyObject *__pyx_arg_radians) {
      |                  ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/core.c:1148:18: note: previous definition of ‘__pyx_pw_11cfunc_dot_to_py_36__Pyx_CFunc_double____double___to_py_1wrap’ was here
 1148 | static PyObject *__pyx_pw_11cfunc_dot_to_py_36__Pyx_CFunc_double____double___to_py_1wrap(PyObject *__pyx_self, PyObject *__pyx_arg_degrees) {
      |                  ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/core.c:1317:18: error: redefinition of ‘__pyx_pf_11cfunc_dot_to_py_36__Pyx_CFunc_double____double___to_py_wrap’
 1317 | static PyObject *__pyx_pf_11cfunc_dot_to_py_36__Pyx_CFunc_double____double___to_py_wrap(PyObject *__pyx_self, double __pyx_v_radians) {
      |                  ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/core.c:1169:18: note: previous definition of ‘__pyx_pf_11cfunc_dot_to_py_36__Pyx_CFunc_double____double___to_py_wrap’ was here
 1169 | static PyObject *__pyx_pf_11cfunc_dot_to_py_36__Pyx_CFunc_double____double___to_py_wrap(PyObject *__pyx_self, double __pyx_v_degrees) {
      |                  ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/core.c:1370:18: error: redefinition of ‘__Pyx_CFunc_double____double___to_py’
 1370 | static PyObject *__Pyx_CFunc_double____double___to_py(double (*__pyx_v_f)(double)) {
      |                  ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/core.c:1222:18: note: previous definition of ‘__Pyx_CFunc_double____double___to_py’ was here
 1222 | static PyObject *__Pyx_CFunc_double____double___to_py(double (*__pyx_v_f)(double)) {
      |                  ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/core.c:1317:18: warning: ‘__pyx_pf_11cfunc_dot_to_py_36__Pyx_CFunc_double____double___to_py_wrap’ defined but not used [-Wunused-function]
 1317 | static PyObject *__pyx_pf_11cfunc_dot_to_py_36__Pyx_CFunc_double____double___to_py_wrap(PyObject *__pyx_self, double __pyx_v_radians) {
      |                  ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/core.c:1296:18: warning: ‘__pyx_pw_11cfunc_dot_to_py_36__Pyx_CFunc_double____double___to_py_1wrap’ defined but not used [-Wunused-function]
 1296 | static PyObject *__pyx_pw_11cfunc_dot_to_py_36__Pyx_CFunc_double____double___to_py_1wrap(PyObject *__pyx_self, PyObject *__pyx_arg_radians) {
      |                  ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/core.c:1222:18: warning: ‘__Pyx_CFunc_double____double___to_py’ defined but not used [-Wunused-function]
 1222 | static PyObject *__Pyx_CFunc_double____double___to_py(double (*__pyx_v_f)(double)) {
      |                  ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
error: command 'x86_64-linux-gnu-gcc' failed with exit status 1

正如 DavidW 所指出的,这可能是一个错误。看起来,在生成的 C 代码中使用的变量名只采用变量类型而不是函数的名称,因此 __pyx_doc_11cfunc_dot_to_py_36__Pyx_CFunc_double____double___to_py_wrap 被定义了两次:一次是 radians 一次是 degrees.

因此您暂时应该使用变通方法。一种是给双打不同的名字,例如:

%%cython -a  --verbose
cdef extern from *:
    """
    //some dummy implementations
    double radians(double degrees)
    {
        return degrees * (1.0 / 180.0);
    }

    int degrees(double radians)
    {
        return radians * (180.0 / 1.0);
    }
    """
    ctypedef double double1 "double"
    cpdef double degrees(double radians)
    cpdef double1 radians(double degrees)

这里我们使用 cname 技巧,Cython 将用“double”替换“double1”。

但这看起来不像是更大 scale/long 术语的理智解决方案。我会切换到更冗长但不那么令人费解的方法(假设在 extern (here documentation) 中使用 cpdef 无论如何都不是很常见):

%%cython -a

cdef extern from *:
    """
    // code as above
    ...
    """
    double c_degrees "degrees"(double radians)
    double c_radians "radians"(double degrees)
    
cpdef double degrees(double radians):
    return c_degrees(radians)
cpdef double radians(double degrees):
    return c_radians(degrees)

再一次,cname-trick 用于区分 cpdef 和 Cython 代码中函数的 wrappend cname。