当前位置: 首页 > 知识库问答 >
问题:

使用python对象列表,通过ctypes从Python调用C代码

充星腾
2023-03-14

我想

  1. 用 C 语言编写一个sum_list求和列表的函数,名为 sum_list.c
  2. sum_list.c 文件设为 sum_list.so
  3. sum_list = ctypes。CDLL('./sum_list.so')
  4. result = sum_list.sum_list([1, 2, 3])

它在步骤4中引发错误:

ctypes。ArgumentError:参数1::不知道如何转换参数1

当我用c编写一个函数,将两个数字相加时,它可以正常工作。

所以,重点是我不知道怎么把一个list (python对象)传递给c。

sum_list.c

#define PY_SSIZE_T_CLEAN
#include <Python.h>

long sum_list(PyObject *list)
{
    Py_ssize_t i, n;
    long total = 0, value;
    PyObject *item;

    n = PyList_Size(list);
    if (n < 0)
        return -1; /* Not a list */
    for (i = 0; i < n; i++) {
        item = PyList_GetItem(list, i); /* Can't fail */
        if (!PyLong_Check(item)) continue; /* Skip non-integers */
        value = PyLong_AsLong(item);
        if (value == -1 && PyErr_Occurred())
            /* Integer too big to fit in a C long, bail out */
            return -1;
        total += value;
    }
    return total;
}

python代码

from ctypes import CDLL

sum_list = CDLL('./sum_list.so')

l = [1, 2, 3]
sum_list.sum_list(l)

我希望我的 python 代码中的 var 结果是 6

共有3个答案

温嘉赐
2023-03-14

正如另一个答案所提到的,您可以使用ctypes.py_object来表示一个实际的Python对象,但是您还必须记住,在调用Python C API时,您不能释放GIL(全局解释器锁)。使用PyDLL而不是CDLL来完成这一点。

这是一个完全工作的示例,适用于一般的序列:

测试c

#include <Python.h>

#ifdef _WIN32
#   define API __declspec(dllexport)
#else
    define API
#endif

API long sum_list(PyObject* o)
{
    Py_ssize_t i, n;
    long total = 0, value;
    PyObject* list;
    PyObject* item;

    list = PySequence_Fast(o,"not a sequence");  // New reference!  Must DECREF eventually.
    if(list == NULL)
        return -1;
    n = PySequence_Fast_GET_SIZE(list);
    for (i = 0; i < PySequence_Fast_GET_SIZE(list); i++) {
        item = PySequence_Fast_GET_ITEM(list, i);
        if (!PyLong_Check(item))
        {
            PyErr_SetString(PyExc_TypeError,"sequence contains non-integer");
            Py_DECREF(list);
            return -1;
        }
        value = PyLong_AsLong(item);
        if (value == -1 && PyErr_Occurred())
        {
            Py_DECREF(list);
            return -1;
        }
        total += value;
    }
    Py_DECREF(list);
    return total;
}

test.py

from ctypes import *

dll = PyDLL('test')
dll.sum_list.restype = c_long
dll.sum_list.argtypes = py_object,

print(dll.sum_list((1,2,3,4,5)))
try:
    print(dll.sum_list(7))
except TypeError as e:
    print(e)
try:
    print(dll.sum_list((1,2,'a',4,5)))
except TypeError as e:
    print(e)
try:
    print(dll.sum_list((1,2,0x80000000,4,5)))
except OverflowError as e:
    print(e)
try:
    print(dll.sum_list((1,2,-0x80000001,4,5)))
except OverflowError as e:
    print(e)

输出:

15
not a sequence
sequence contains non-integer
Python int too large to convert to C long
Python int too large to convert to C long
呼延庆
2023-03-14

我已经投票给了@Antii的答案,但仍然想添加一个简短、完整且独立的示例。

首先,ctypes是与C代码交互,而C代码是在没有考虑Python互操作性的情况下编写的。通常,这意味着代码不包含

/**
 * Sample function to call from Python 3
 * Build:
 *  $ gcc -shared -o libmath.so -fPIC math.c
 *
 * See https://stackoverflow.com/a/14884166/4594973
 *
 **/
int add(int x, int y) {
    return x + y;
}

虽然上面的代码很简单,但是您需要确保更复杂的代码能够正确编译。例如,如果您正在使用C编译器和/或正在使用C特有的特性(例如,类、方法重载等),那么您必须在构建库时公开一个C兼容的接口,因为C的名字混淆将使得事后几乎不可能找到函数。

在构建了上面的C代码之后,使用这个Python脚本来调用它:

#!/usr/bin/env python3

import ctypes

cmath = ctypes.CDLL('./libmath.so')
cmath.restype = ctypes.c_int
cmath.argtypes = [ctypes.c_int, ctypes.c_int]

x = 4
y = 5
r = cmath.add(x, y)

print(f'{x} + {y} = {r}')

从我的终端运行示例:

$ gcc -shared -o libmath.so -fPIC math.c
$ python3 math.py
4 + 5 = 9
唐涛
2023-03-14

ctypes库用于调用纯C库中的函数。您找到的示例是一个函数,它将进入Python扩展模块,并且可以在没有ctypes的情况下使用。Ctypes实际上有一个PyObject*的类型,即Ctypes.py_object。你需要将列表包装在这张纸上——小心确保它还活着!

在任何情况下,您几乎都必须使用resttypeargtype来提供函数的正确原型。

这是一个工作示例:

import ctypes

sum_list = ctypes.PyDLL('./sum_list.so')
sum_list.restype = ctypes.c_long
sum_list.argtypes = [ ctypes.py_object ]

l = [1, 2, 3]
print(sum_list.sum_list(ctypes.py_object(l)))

注意,正如Mark注意到的,这里必须使用< code>PyDLL,以确保GIL不会被释放,因为函数没有获得它!

 类似资料:
  • 问题内容: 所以我想通过cython从c调用一些python代码。我设法从c调用cython代码。而且我还可以从cython调用python代码。但是,当我将它们全部加在一起时,会丢失一些东西。 这是我的python代码(): 这是我的cython“ bridge”(): 这是c代码(): 运行此命令时,出现以下异常: 我怀疑缺少的部分: 我还没打电话 我还没有 Cython没有产生任何东西- 不

  • 本文向大家介绍Python使用ctypes调用C/C++的方法,包括了Python使用ctypes调用C/C++的方法的使用技巧和注意事项,需要的朋友参考一下 python使用ctypes调用C/C++ 1. ctpes介绍 ctypes is a foreign function library for Python. It provides C compatible data types, a

  • 问题内容: 如果我有以下两套代码,如何将它们粘合在一起? 如何使用x中连续的元素列表调用c_function?我试图将x强制转换为c_void_p,但这没有用。 我也尝试使用类似 但这会出现语法错误。 C代码显然想要一个数组的地址。我如何做到这一点? 问题答案: 以下代码适用于任意列表:

  • 问题内容: 构建与C或C ++库的Python绑定的最快方法是什么? (如果这很重要,我正在使用Windows。) 问题答案: Boost Python库是用于连接Python和C 的框架。它使您可以快速而无缝地将C 类的函数和对象暴露给Python,反之亦然,而无需使用特殊工具-仅使用C 编译器即可。它被设计为以非介入方式包装C 接口,因此您不必为了包装而完全更改C ++代码,从而使Boost.

  • 问题内容: 我是Python的新手,并且正在学习教程。本教程中有一个示例: 现在,在教程中,。但就我而言,我得到以下错误: 问题答案: 好像你已经用指向类实例的相同名称遮盖了指向类的内置名称。这是一个例子: 我相信这是显而易见的。Python将对象名称(函数和类也是对象)存储在字典中(命名空间实现为字典),因此你可以在任何范围内重写几乎任何名称。它不会显示为某种错误。如你所知,Python强调“特

  • 我有一个字符串类型的列,其中包含嵌套字典对象的元素列表。例如 这实际上是火花数据帧中的一列,它是字符串类型的。所以我想知道如何将字符串的内容转换为列表,以便我可以使用json.loads.转换列表中的每个元素 知道吗?