Cython:从 C 程序调用 Python 代码

Posted

技术标签:

【中文标题】Cython:从 C 程序调用 Python 代码【英文标题】:Cython: Calling Python code from C program 【发布时间】:2014-01-07 01:13:58 【问题描述】:

我正在尝试制作 Cython 包装器,以便我可以从 C 调用 Python 代码。我遇到了导入问题,因为我希望包装器与原始代码分开。

以下代码在调用导入函数时以 segfault 结尾。如果代码被编写为 python 模块并通过import 导入,则程序会说name ... is not defined。当所有内容都在一个文件中并且不涉及导入时,问题不会出现(实际上,Cython 生成的代码在 cimporting 时会失败)。当从其他 python 脚本(编译为 .so 或实时)导入 libcimpy.pyx 时,代码也可以正常工作

我准备了一个最小的例子。这与实际代码相去甚远,但涵盖了原理。

cimpPy.pyx:示例 python 代码(转换为 Cython)

cdef sum(a, b):
    return a + b

cimpPy.pxd

cdef sum(a, b)

libcimpy.pyx(粘贴 Cython 代码)

cimport cimpPy

cdef public int cSum(int a, int b):
    return cimpPy.sum(a, b)

ci.c(我们要从中调用 cimpPy 的 c 代码)

#include <stdio.h>
#include <stdlib.h>
#include <Python.h>
#include "libcimp.h"

int main(int argc, char **argv) 
  Py_Initialize();
  initlibcimp();
  int a = 2;
  int b = 3;
  int c = cSum(a, b);
  printf("sum of %d and %d is %d\n", a, b, c);
  Py_Finalize();
  return 0;

生成文件

EXECUTABLE = ci

OBJS       = ci.o

CC         = gcc
CFLAGS     = -g -I/usr/include/python2.7 -I$(shell pwd)

LINKER     = g++
LDFLAGS    = -L$(shell pwd) $(shell python-config --ldflags) -lcimp
.PHONY: clean cython

all: cython $(EXECUTABLE)

cython:
    python setup.py build_ext --inplace

.c.o:
    $(CC) $(CFLAGS) -c $<

$(EXECUTABLE) : $(OBJS)
    $(LINKER) -o $(EXECUTABLE) $(OBJS) $(LDFLAGS)

clean:
    rm -rf *.o *.so libcimp.c libcimp.h core build $(EXECUTABLE)

setup.py

from distutils.core import setup, Extension
from Cython.Build import cythonize
from Cython.Distutils import build_ext

extensions = [
    Extension("libcimp", ["libcimp.pyx"])
]

setup(
    name = "CIMP",
    cmdclass = "build_ext": build_ext,
    ext_modules = cythonize(extensions)
)

我打算实现的是能够将 Python 代码插入到更大的 C 系统中。假设是用户将能够自己编写 Python。 C 代码是一个模拟引擎,可以在环境中对代理进行操作。这个想法是代理和环境的行为可以在 python 中指定,并在必要时传递给引擎进行评估。最好的类比是一个 map reduce 系统,其中 Python 脚本是映射器。从这个意义上说,我想从 C 调用 Python,而不是反过来。

将所有内容都转换为 Cython,而引人注目的是大型任务。

这是正确的方法吗?为什么导入仅在 python 解释器下有效,而在外部嵌入时无效?任何建议和参考文章或文档表示赞赏。

【问题讨论】:

docs.python.org/2/extending/… 回复“这是正确的做法吗?”,试图澄清情况。如果您打算让用户使用 Python 编写代码,并为您的 C 模拟引擎提供 Python 接口,那么您正在做相反的事情。上面,您正在尝试从 C 接口 Python 代码,反之亦然。正如您所说,如果您想“将 Python 代码插入到更大的 C 系统中”,那就可以了。但是如果你想为引擎创建一个接口,以便用户可以用python对其进行编程,你想从python调用c,而不是从c调用python。 @flebool 在您回复后我意识到我对我想要实现的目标提供了不太好的解释。 C 代码是一个模拟引擎,可以在环境中对代理进行操作。这个想法是在 python 中指定代理和环境的行为,并在必要时传递给引擎进行评估。最好的类比是一个 map reduce 系统,其中 Python 脚本是映射器。从这个意义上说,我想从 C 调用 Python,而不是反过来。 【参考方案1】:

在这段代码中,initlibcimp()实际上是失败的,但是你并没有马上看到,因为错误是通过设置python异常来报错的。我不是 100% 确定这是执行此操作的正确方法,但我可以通过在该调用下方添加以下代码来查看错误:

if (PyErr_Occurred())

    PyErr_Print();
    exit(-1);

然后,程序会输出:

Traceback (most recent call last):
  File "libcimpy.pyx", line 1, in init libcimpy (libcimpy.c:814)
    cimport cimpPy
ImportError: No module named cimpPy

cimpPy 模块尚未定义的原因是您需要在调用initlibcimp 之前调用initcimpPy()

【讨论】:

以上是关于Cython:从 C 程序调用 Python 代码的主要内容,如果未能解决你的问题,请参考以下文章

cython与python的不同都有哪些

cython和python的区别

使用 python-c-api 调用的 Cython 回调段错误

Cython 是用于构建 C 代码还是用于构建 Python 扩展?

『Python CoolBook』Cython

在 C++ 程序中调用 Cython (Python3.6)