通过cython从c调用python代码

piu*_*ius 19 c python cython

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

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

def quack():
    print "Quack!"
Run Code Online (Sandbox Code Playgroud)

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

from quacker import quack

cdef public void call_quack():
    quack()
Run Code Online (Sandbox Code Playgroud)

这是c代码(main.c):

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

int main() {
  Py_Initialize();
  initcaller();
  call_quack();
  Py_Finalize();
  return 0;
}
Run Code Online (Sandbox Code Playgroud)

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

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
Run Code Online (Sandbox Code Playgroud)

brm*_*brm 10

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

Exception NameError: "name 'quack' is not defined" in 'caller.call_quack' ignored
Run Code Online (Sandbox Code Playgroud)

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

$ PYTHONPATH=".:$PYTHONPATH" ./main 
Quack!
Run Code Online (Sandbox Code Playgroud)

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

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

PyRun_SimpleString("import sys\nsys.path.insert(0,'')");
Run Code Online (Sandbox Code Playgroud)

重新编译后,只需运行即可./main.

编辑

如果你按照问题中的规定运行代码,那么看看会发生什么真的很有趣,所以不重命名quacker.pyx文件.在这种情况下,该initcaller()函数尝试导入quacker模块,但由于没有quacker.pyquacker.pyc存在,无法找到该模块,并且该initcaller()函数会产生错误.

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

if (PyErr_Occurred())
{
    PyErr_Print();
    return -1;
}
Run Code Online (Sandbox Code Playgroud)

然后程序的输出变为:

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
Run Code Online (Sandbox Code Playgroud)

通过之前调用该initquacker()函数,模块名称已经被注册,因此在里面完成的导入调用将检测到它已经加载并且调用将成功. initcaller()quackerinitcaller()


小智 7

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

主文件

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

int
main() 
{
    PyImport_AppendInittab("caller", PyInit_caller);
    Py_Initialize();
    PyImport_ImportModule("caller");
    call_quack();
    Py_Finalize();
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

调用者.pyx

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

from quacker import quack

cdef public void call_quack():
    quack()
Run Code Online (Sandbox Code Playgroud)

骗子.py

def quack():
    print("Quack!")
Run Code Online (Sandbox Code Playgroud)

最后,这是编译所有内容的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__
Run Code Online (Sandbox Code Playgroud)