【问题标题】:Cython: "Cannot convert Python object" errorCython:“无法转换 Python 对象”错误
【发布时间】:2017-10-29 21:24:32
【问题描述】:

我正在尝试让一个测试项目工作,该项目通过 Python 调用一个带有数组参数的 C 函数:

test.cpp

void testFn(int arr[]);

void testFn(int arr[])
{
    arr[0] = 1;
    arr[1] = 2;
} 

caller.pyx

import ctypes

cdef extern from "test.cpp":
    void testFn(int arr[])

def myTest():
    a = [0, 0]
    arr = a.ctypes.data_as(ctypes.POINTER(ctypes.c_integer))
    testFn(arr)
    print(arr)

setup.caller.py

from distutils.core import setup
from distutils.extension import Extension
from Cython.Distutils import build_ext

sourcefiles = ['caller.pyx']
ext_modules = [Extension("caller", sourcefiles)]

setup(
    name = 'test app',
    cmdclass = {'build_ext': build_ext},
    ext_modules = ext_modules
)

但是当我尝试构建项目时出现错误:

$ python setup.caller.py build_ext --inplace
running build_ext
cythoning caller.pyx to caller.c

Error compiling Cython file:
------------------------------------------------------------
...
def myTest():
    a = [0, 0]
    arr = a.ctypes.data_as(ctypes.POINTER(ctypes.c_integer))
    testFn(arr)
          ^
------------------------------------------------------------

caller.pyx:13:11: Cannot convert Python object to 'int *'

【问题讨论】:

  • 你需要给arr一个类型。 cdef int* arr 应该可以解决问题。

标签: python cython


【解决方案1】:

这个问题经常出现,但我找不到一个好的重复目标,这超出了“只要这样做,它就会起作用”。

这是一种非常常见的情况:您尝试将一些 python 数据结构传递给需要指针 int *, double *,... 的 c 代码。但是指针不是 python 对象,所以我们不能将它们从/传递到 python 代码。

Cython 可以自动处理到intdoublefloat 等的转换,甚至是char *(它是一个以null 结尾的c 字符串)和一些stl-containers,但不能转换为指针( char *是一个例外)。

有两种最常见的情况:

  1. python-data 的内部表示已经是一个 c 数组(array.array,numpy-arrays)。
  2. 内部表示不是连续数组(例如list

1.通过内存视图传递:

在 python 中我们无法以某种方式获取指针,所以这必须在 cython 中完成。我将array.arraynumpy.array(或任何其他支持buffer-protocol 的python 对象,对于ctypes.Array 参见例如这个SO-question)传递给cython 函数的第一选择是内存视图:

def myTest(int[:] arr):
    testFn(&arr[0])

现在从 python 调用它:

>>> import array
>>> a=array.array('i', [0]*2)
>>> import caller
>>> caller.myTest(a)
>>> a
array('i', [1, 2]) #it worked

以下很重要

  1. int[:] 是一个 Python 对象(类型化内存视图),因此可以将其传递给 Python 函数(defcpdef)。
  2. &arr[0] 用于获取内存视图的缓冲区地址。其结果类型为int *
  3. 函数的调用是“类型安全的”,你不能将array.array('I', [0]*2)传递给它,因为它不是int-memory-view,而是unsigned int-memory-view。李>

2。传递非连续内存(例如列表):

list 和 Co. 还有更多工作:信息不存储在普通 c 数组中,因此我们需要先将它们复制到连续内存中,将此临时变量传递给我们的 c 代码并复制结果回到列表,我们的 cython 函数如下所示

import array
def myTest2(lst):
    tmp=array.array('i', lst)
    myTest(tmp)
    lst.clear()
    lst.extend(tmp)

现在重新加载 caller 模块后:

>>> lst=[0,0]
>>> caller.myTest2(lst)
[1, 2]

因此,可以将列表的内容传递给 c 函数,但如果您需要与 c 代码进行数据交换,基本上您希望使用 array.arraynumpy.array

【讨论】:

    猜你喜欢
    • 2012-03-16
    • 2013-06-05
    • 1970-01-01
    • 2020-04-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-03-28
    相关资源
    最近更新 更多