Пример программы 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 (и каталог для сборки). Ну, я хотел бы использовать переведенную (я надеюсь) функцию foo в main.c. Что я должен поместить в файл main.c и как его скомпилировать, чтобы можно было использовать функцию foo? Я использую GCC.
1 ответ
Далеко не эксперт AC, но для меня, использующего 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, его следует вызывать перед использованием любых других функций API Python / C;), вы получите:
Fatal Python error: PyThreadState_Get: no current thread
Aborted (core dumped)
Без initfoo()
или же import_foo()
Вы получаете:
Segmentation fault (core dumped)
Если вы не вызываете Py_Finalize:
Py_Initialize
no-op при повторном вызове (без вызова Py_Finalize() первым).
Чтобы получить delorean пример из документации для запуска:
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
со структурой транспортного средства или в основном или использовать я не использовал 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 или наоборот согласно документам. Однако обратите внимание, что вы должны включить либо modulename.h, либо modulename_api.h в данный файл C, а не оба, иначе вы можете получить конфликтующий двойной определения.
Чтобы сделать то же самое с делорейским примером, я должен был использовать 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;
}