在Python中调用C函数

36

我有一堆用C语言编写的函数,我希望我用Python语言编写的代码能够访问这些函数。

我在这里看到了几个与此类似的问题(例如这里这里),但是我对需要采取哪种方法感到困惑。

一个问题建议使用ctypes,另一个问题建议使用cython。我已经阅读了两者的文档,但我完全不清楚哪个更适合我。

基本上,我已经编写了一些Python代码来执行二维FFT,我希望C代码能够查看该结果,然后通过我编写的各种C函数进行处理。我不知道从C调用Python还是反过来是否更容易。


1
你有库文件吗?.so文件? - ManuParra
1
共享库?g++ -shared -Wl,-soname,mylib.so -o mylib.so my.o? - ManuParra
6个回答

69
您可以通过编写 ctypes 包装器来从 Python 调用 C。Cython 是为了使类似 Python 的代码运行速度更快,而 ctypes 则是为了使 C 函数可从 Python 调用。你需要做的是以下几步:
  1. 编写您想要使用的 C 函数。(您可能已经完成此步骤)
  2. 为这些函数创建共享对象(.so,适用于 Linux、OS X 等)或动态加载库(.dll,适用于 Windows)。 (也许您已经完成了这一步)
  3. 编写 ctypes 包装器(比听起来容易得多,我为此写了一篇如何文章
  4. 在 Python 中调用该包装器中的函数。(这与调用任何其他 Python 函数一样简单)

1
假设 OP 是 C 和 Python 代码的作者,并且 OP 偏好编写 Python 代码,那么不清楚这里应该使用什么。Cython 可以允许从 C 中提取更多的代码。 - jfs
1
+1 有关评论。而Cython不仅仅是性能问题。它是一个复杂而强大的工具。它允许您将C代码与Python代码混合使用,因此如果您恰好在C中编码,则可以成为C和Python之间的完美桥梁。也就是说,您可以将静态(强类型变量)与动态类型变量混合使用。并通过简单的“转换”双向切换从C(PyObject)表示到Python(object)表示。 - Gauthier Boaglio
链接中的教程非常好,但我不得不将“int sum”更改为“int sum = 0”,才能使其正常工作。 - Akavall
在第三部分中,当你执行以下操作时:array_type = ctypes.c_int * num_numbers result = _sum.our_function(ctypes.c_int(num_numbers), array_type(*numbers))这个过程会创建 numbers 列表的副本吗? - mercury0114
@mercury0114 这将创建一个新的 ctypes 数组,其中包含列表中的值,并将该数组传递给函数。 - Florian Rhiem
显示剩余2条评论

10

如果我理解正确,你对于像 c => python 或者 python => c 的对话没有偏好。

在这种情况下,我建议使用 Cython。它非常适合各种操作,特别是在你的情况下,从 C 调用已经在 Python 中编写的函数。

以下是它的工作方式(public api):

以下示例假定你有一个 Python 类(self 是它的实例),并且该类有一个方法(名为 method),你想在 C 中调用该方法并处理结果(这里是一个 double)。这个函数是在一个 Cython 扩展 中编写的,可以帮助你进行这个调用。

cdef public api double cy_call_func_double(object self, char* method, bint *error):
    if (hasattr(self, method)):
        error[0] = 0
        return getattr(self, method)();
    else:
        error[0] = 1

在C端,您可以这样执行调用:
PyObject *py_obj = ....
...
if (py_obj) {
    int error;
    double result;
    result = cy_call_func_double(py_obj, (char*)"initSimulation", &error);
    cout << "Do something with the result : " << result << endl;
}

在Python / C API中提供了一个名为PyObject的结构体。在您的Cython扩展中,通过将常规python object强制转换为<PyObject *>my_python_object,您可以捕获py_obj,最终可以在其上调用initSimulation方法并对结果进行操作(这里是一个double,但Cython可以轻松处理vectorssets,...)。

如果您从未使用过Cython编写任何内容,则我知道我刚刚写的可能会令人困惑,但它旨在简短地演示它在合并方面可以为您做的许多事情。

另一方面,这种方法可能需要比将Python代码重新编码为C更长的时间,这取决于您的算法复杂度。在我看来,只有当您计划经常有这种需求时,投资学习Cython才是相关的。希望这至少是有信息量的...

1
谢谢!看起来我用同样的努力就可以在C语言中重新编码,所以在这种情况下我不必使用Cython。但我猜我将来会发现这些信息非常有用。 - Kitchi
这是C还是C++?cout<iostream>的一部分,对吗? - Ayxan Haqverdili

5

这里涉及到以下两个问题:

  1. 如何在Python中调用C函数(扩展Python)
  2. 如何在C程序中调用Python函数/脚本(嵌入Python)

对于第二个问题即“嵌入Python”,您可以使用以下代码片段:

#include "python.h"

int main(int argc, char *argv[]) {   
Py_SetProgramName(argv[0]);  /* optional but recommended */   
Py_Initialize();   
PyRun_SimpleString("from time import time,ctime\n"
    "print 'Today is',ctime(time())\n");
/*Or if you want to run python file within from the C code*/       
//pyRun_SimpleFile("Filename");    
Py_Finalize();   
return 0; }

对于第一个问题,即'扩展 Python'

最好的方法是使用 Ctypes(顺便说一下,它在所有变体的 Python 中都可移植)。


>> from ctypes import *

>> libc = cdll.msvcrt

>> print libc.time(None)

>> 1438069008

>> printf = libc.printf

>> printf("Hello, %s\n", "World!")

>> Hello, World!
14

>> printf("%d bottles of beer\n", 42)

>> 42 bottles of beer
19

如果您想要详细的指南,可以参考我的博客文章


2

从Python调用C会更容易。您的情况听起来有些奇怪 - 通常人们大部分代码都是用Python编写的,除了处理计算密集型部分,这部分会使用C语言编写。两维FFT是否是您代码中计算密集型的部分?


1
这是计算强度最小的部分...只是事实证明,使用Python编写代码比使用C更方便。我希望编写函数的封装比重新用C重写整个函数更快。 - Kitchi

0

这里有一篇简洁明了的教程,来自Digital Ocean。简要版:

1. 编写 C 代码
你已经完成了这个步骤,以下是一个超级简短的示例:

#include <stdio.h>

int addFive(int i) {
    return i + 5;
}

2. 创建共享库文件
假设上述C文件已保存为c_functions.c,则在终端中输入以下命令以生成.so文件以从Python调用: cc -fPIC -shared -o c_functions.so c_functions.c

3. 在Python中使用您的C代码!
在您的Python模块中:

# Access your C code
from ctypes import *
so_file = "./c_functions.so"
c_functions = CDLL(so_file)

# Use your C code
c_functions.addFive(10)

最后一行将输出15。你完成了!


0

BLimitless引用Digital Ocean的答案对于简单情况是可以的,但它默认只允许int类型的参数。如果需要不同类型的输入参数,例如float类型,您需要添加以下内容:

c_functions.addFive.argtypes=[ctypes.c_float]

如果你改变输出参数,例如改为浮点数类型,你需要这样做:

c_functions.addFive.restype=ctypes.c_float


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接