通过cython从c调用python代码

2024-10-05 12:25:48 发布

您现在位置:Python中文网/ 问答频道 /正文

所以我想通过cython从c调用一些python代码。我设法从c调用了cython代码,也可以从cython调用了python代码。但当我把它们加在一起时,有些东西就不见了。

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

def quack():
    print "Quack!"

这是我的cython“桥”(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

Tags: 代码pyincludemainlibraryframeworkcallversions
3条回答

也许这不是你想要的,但我是通过以下改变来实现的:

在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;
}

如果将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变量中,但是在使用Py_InitializePy_Finalize创建简单程序时,似乎不会发生这种情况。由于PYTHONPATH变量还用于填充sys.pathpython变量,因此上面的解决方法会产生正确的结果。

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

PyRun_SimpleString("import sys\nsys.path.insert(0,'')");

重新编译之后,只需运行./main就可以了。

编辑

如果您按照问题中指定的方式运行代码,那么在不重命名quacker.pyx文件的情况下,查看发生了什么事情实际上是很有趣的。在这种情况下,initcaller()函数尝试导入quacker模块,但由于不存在quacker.pyquacker.pyc,因此找不到该模块,并且initcaller()函数会产生错误。

现在,通过引发异常,以python的方式报告此错误。但是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

通过在initcaller()之前调用initquacker()函数,模块名quacker已经注册,因此在initcaller()内完成的导入调用将检测到它已经加载,调用将成功。

如果有人想知道它在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;
}

呼叫者.pyx

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

from quacker import quack

cdef public void call_quack():
    quack()

嘎嘎声.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__

相关问题 更多 >

    热门问题