繁体   English   中英

通过 cython 从 c 调用 python 代码

[英]Call python code from c via cython

所以我想通过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/编译/链接/运行的方式:

$ 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

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

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

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

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

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

或者,在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 新手挣扎后的解决方案。

主文件

#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__

也许这不是您想要的,但我通过以下更改使其工作:

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

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM