问题 Cython作为Python到C转换器的示例程序


我发现 这里 和 这里 可以使用Cython将Python转换为C,但我找不到任何一步一步的例子。假设我有一个简单的功能:

foo.pyx

cdef void foo(double* x):
   x[0] = 0.0

setup.py

from distutils.core import setup
from Cython.Build import cythonize

setup(
    ext_modules = cythonize("foo.pyx")
)

然后我跑: python setup.py build_ext --inplace 获取foo.c和foo.so文件(以及构建目录)。好吧,我想在main.c中使用翻译(我希望)的foo函数。我应该把什么放入main.c文件以及如何编译它以便能够使用foo函数?我正在使用gcc。


5970
2017-12-27 13:40


起源

我试过了 gcc -L。 -Wall -o main main.c -lpython2.7 -l:foo.so,但我想我在main.c文件中遗漏了一些东西,因为我得到了“未定义的引用`foo'” - Bociek
你想从main.c调用foo吗?喜欢 docs.cython.org/src/userguide/...? - Padraic Cunningham
@PadraicCunnigham确切地说!我想要foo(x)电话。 - Bociek
所以,我只需要添加 API 关键字和一切有效吗?我应该检查一下。 - Bociek
对不起@PadraicCunningham,但我不知道如何编译它。我包含了“foo_api.h”和import_foo(),但是我得到了Segmentation Fault - Bociek


答案:


远非c专家,但对于我使用ubuntu,以下工作:

main.c中:

#include "foo_api.h"
#include <stdio.h>


int main(int argc, char *argv[]) {
     Py_Initialize();
     initfoo();
     import_foo();
     double arr[5] = {1,2,3,4,5};
     int i = 0;
     foo(arr);
     for(i = 0; i < 5; i++)
    {
      printf("%f\n", arr[i]);
    }
     Py_Finalize();
     return 0;
}

foo.pyx:

cdef public api  foo(double* x):
   x[0] = 0.0

从同一目录:

$ cython foo.pyx 

然后:

$ cc -I/usr/include/python2.7 -I/usr/include/x86_64-linux-gnu/python2.7   -o foo  *.c -lpython2.7 

然后跑吧。

$ ./foo
0.000000
2.000000
3.000000
4.000000
5.000000

我用了 pkg-config --cflags python 得到旗帜:

 $ pkg-config --cflags python
-I/usr/include/python2.7 -I/usr/include/x86_64-linux-gnu/python2.7 

没有打电话 Py_Initialize (初始化Python解释器。在嵌入Python的应用程序中,应该在使用任何其他Python / C API函数之前调用它;), 你会得到:

Fatal Python error: PyThreadState_Get: no current thread
Aborted (core dumped)

没有 initfoo() 要么 import_foo() 你得到一个:

 Segmentation fault (core dumped)

如果你不打电话 Py_Finalize

Py_Initialize  第二次调用时没有操作(没有先调用Py_Finalize())。

得到的 德罗宁 从运行的文档中的示例:

main.py:

#include "delorean_api.h"
#include <stdio.h>
Vehicle car;


int main(int argc, char *argv[]) {
     Py_Initialize();
     initdelorean();
     import_delorean();
     car.speed = atoi(argv[1]);
     car.power = atof(argv[2]);
     activate(&car);
     Py_Finalize();
     return 0;
}

delorean.pyx:

ctypedef public struct Vehicle:
    int speed
    float power

cdef api void activate(Vehicle *v):
    if v.speed >= 88 and v.power >= 1.21:
        print "Time travel achieved"
    else:
        print("Sorry Marty")

程序是一样的,唯一的变化是我必须使用 ctypedef 使用Vehicle结构或主要或使用我使用 struct Vehicle car; 在主要:

$ cython delorean.pyx
$ cc  -I/usr/include/python2.7 -I/usr/include/x86_64-linux-gnu/python2.7   -o delorean  *.c -lpython2.7  
$ ./delorean 1 1
Sorry Marty
$ ./delorean 100 2
Time travel achieved

您也可以在不使用的情况下使用它 Py_Initialize 等等...

foo.pyx 你只需要公开这个功能:

cdef public  foo(double* x):
   x[0] = 0.0

我补充道 #include <python2.7/Python.h> 刚进口 foo.h在main.c中删除 Py_Initialize(); 等。只需导入 python.h 不会对我有用,但对所有人来说可能并非如此。

#include <python2.7/Python.h>
#include "foo.h"
#include <stdio.h>


int main(int argc, char *argv[]) {
     double arr[5] = {1,2,3,4,5};
     int i = 0;
     foo(arr);
     for(i = 0; i < 5; i++)
    {
      printf("%f\n", arr[i]);
    }

     return 0;
}

编译是一样的:

$ cython foo.pyx 
$ cc -I/usr/include/python2.7 -I/usr/include/x86_64-linux-gnu/python2.7   -o foo  *.c -lpython2.7 
$ ./foo
0.000000
2.000000
3.000000
4.000000
5.000000

如果您使用的是api版本,那么只需按照文档包含api标头或反之亦然 但是,请注意,您应该在给定的C文件中包含modulename.h或modulename_api.h,而不是两者,否则您可能会遇到冲突的双重定义。

要对我必须使用的delorean示例做同样的事情 libc.stdio 打印字符串以避免分段错误:

from libc.stdio cimport printf

ctypedef public  struct Vehicle:
    int speed
    float power

cdef public void activate(Vehicle *v):
    if v.speed >= 88 and v.power >= 1.21:
        printf("Time travel achieved\n")
    else:
        printf("Sorry Marty\n")

主要:

#include <python2.7/Python.h>
#include <stdio.h>
#include "delorean.h"

Vehicle car;


int main(int argc, char *argv[]) {
     car.speed = atoi(argv[1]);
     car.power = atof(argv[2]);
     activate(&car);
     return 0;
}

返回值可能更有意义:

ctypedef public  struct Vehicle:
    int speed
    float power

cdef public  char* activate(Vehicle *v):
    if v.speed >= 88 and v.power >= 1.21:
        return  "Time travel achieved"
    return "Sorry Marty"

主要:

#include <python2.7/Python.h>
#include <stdio.h>
#include "delorean.h"

Vehicle car;

int main(int argc, char *argv[]) {
     car.speed = atoi(argv[1]);
     car.power = atof(argv[2]);
     printf("%s\n",activate(&car));
     return 0;
}

13
2017-12-27 16:38



谢谢你的回答@PadraicCunningham。我虽然可以完全绕过这个Py_Initialize()等东西 德罗宁例。 - Bociek
@Bociek,我在没有Py_Initialize()等的情况下添加了如何做到这一点。 - Padraic Cunningham
尼斯@PadraicCunningham :) - Bociek
你知道如何处理像你这样的库。 numpy,mpmath,scipy?我试图添加 导入numpy为np 并返回np.exp(2.0)但我使用第二种方法得到了Segmentation Fault而没有api字(只有公共)。 - Bociek
@Bociek,你试过了 return np.exp(2.0) 用哪个? - Padraic Cunningham