通过cython从c调用python代码

时间:2014-03-23 10:51:26

标签: python c cython

所以我想从c通过cython调用一些python代码。我设法从c调用cython代码。我也可以从cython中调用python代码。但是当我把它们全部加在一起时,有些东西会丢失。

这是我的python代码(quacker.pyx):

def quack():
    print "Quack!"

这是我的cython“bridge”(caller.pyx):

from quacker import quack

cdef public void call_quack():
    quack()

这是c代码(main.c):

#include <Python.h>
#include "caller.h"

int main() {
  Py_Initialize();
  initcaller();
  call_quack();
  Py_Finalize();
  return 0;
}

当我运行时,我得到了这个例外:

Exception NameError: "name 'quack' is not defined" in 'caller.call_quack' ignored

我怀疑的缺失部分:

  • 我没有致电initquacker()
  • 我没有收录quacker.h
  • Cython没有产生任何quacker.h - only quacker.c
  • caller.c未导入quacker.h或致电initquacker()

我不确定我甚至可以做我想做的事情,但在我看来它应该是。我很乐意听到您的任何意见。

编辑:

这是我cythonize / compile / link / run的方式:

$ cython *.pyx
$ cc -c *.c -I/System/Library/Frameworks/Python.framework/Versions/2.7/include/python2.7
$ cc -L/System/Library/Frameworks/Python.framework/Versions/2.7/lib -L/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/config -lpython2.7 -ldl *.o -o main
$ ./main

4 个答案:

答案 0 :(得分:10)

如果您将quacker.pyx重命名为quacker.py,则一切都是正确的。唯一的问题是你的程序不会在当前目录中搜索python模块,从而产生输出:

Exception NameError: "name 'quack' is not defined" in 'caller.call_quack' ignored

如果将当前目录添加到PYTHONPATH环境变量中,输出将成为您期望的输出:

$ PYTHONPATH=".:$PYTHONPATH" ./main 
Quack!

运行python shell时,根据documentation,当前目录(或包含脚本的目录)会自动添加到sys.path变量,但是在使用{{1}创建简单程序时}}和Py_Initialize这似乎不会发生。由于PYTHONPATH变量也用于填充Py_Finalize python变量,因此上面的解决方法会产生正确的结果。

或者,在sys.path行下面,您可以通过执行一些指定为字符串的python代码,将空字符串添加到Py_Intialize,如下所示:

sys.path

重新编译后,只需运行PyRun_SimpleString("import sys\nsys.path.insert(0,'')"); 即可。

修改

如果您运行问题中指定的代码,那么看看会发生什么实际上很有趣,因此无需重命名./main文件。在这种情况下,quacker.pyx函数会尝试导入initcaller()模块,但由于不存在quackerquacker.py,因此无法找到模块,quacker.pyc函数产生错误。

现在,通过引发异常,以python的方式报告此错误。但是initcaller()文件中的代码不会检查这一点。我不是这方面的专家,但在我的测试中,在main.c下添加以下代码似乎有效:

initcaller()

程序的输出如下:

if (PyErr_Occurred())
{
    PyErr_Print();
    return -1;
}

通过在 Traceback (most recent call last): File "caller.pyx", line 1, in init caller (caller.c:836) from quacker import quack ImportError: No module named quacker 之前调用initquacker()函数,模块名称initcaller()已经注册,因此在quacker内完成的导入调用将会检测到它已经加载并且呼叫将成功。

答案 1 :(得分:2)

也许这不是你想要的,但我通过以下改变得到它:

在quacker.pyx中我添加了

cdef public int i

强制Cython生成.h文件。

然后在主要:

#include <Python.h>
#include "caller.h"
#include "quacker.h"

int main() {
  Py_Initialize();
  initquacker();
  initcaller();
  call_quack();
  Py_Finalize();
  return 0;
}

答案 2 :(得分:2)

万一有人想知道它在Python 3中如何工作,这是我作为Cython新手苦苦挣扎后的解决方案。

main.c

#include <Python.h>
#include "caller.h"

int
main() 
{
    PyImport_AppendInittab("caller", PyInit_caller);
    Py_Initialize();
    PyImport_ImportModule("caller");
    call_quack();
    Py_Finalize();
    return 0;
}

caller.pyx

# cython: language_level=3
import sys
sys.path.insert(0, '')

from quacker import quack

cdef public void call_quack():
    quack()

quacker.py

def quack():
    print("Quack!")

最后,这是编译所有内容的 Makefile

target=main
cybridge=caller

CC=gcc
CFLAGS= `python3-config --cflags`
LDFLAGS=`python3-config --ldflags`

all:
        cython $(cybridge).pyx
        $(CC) $(CFLAGS) -c *.c
        $(CC) $(LDFLAGS) *.o -o $(target)

clean:
        rm -f $(cybridge).{c,h,o} $(target).o $(target)
        rm -rf __pycache__

答案 3 :(得分:1)

我需要使用 CMake 执行此操作,并最终重新创建了此示例。您可以找到包含完整工作示例 here 的存储库。

您可以使用 CLI 上的 Docker 或 Visual Studio devcontainer 构建和运行示例。