【发布时间】:2013-03-22 17:09:25
【问题描述】:
为了提高速度,我正在为我的 Python 程序编写一个 C 扩展,并且在尝试传入 3 维 numpy 数组时遇到了一些非常奇怪的行为。它适用于二维数组,但我确信我正在用指针试图让它与第 3 维一起工作。但这是奇怪的部分。如果我只是传入一个 3-D 数组,它会因 Bus Error 而崩溃。如果(在 Python 中)我首先将变量创建为 2D 数组,然后用 3D 数组覆盖它,它可以完美运行。如果变量先是空数组,然后是 3D 数组,则会因 Seg Fault 而崩溃。这怎么可能发生?
另外,谁能帮我让 3D 阵列正常工作?还是我应该放弃并传入一个二维数组并自己重塑它?
这是我的 C 代码:
static PyObject* func(PyObject* self, PyObject* args) {
PyObject *list2_obj;
PyObject *list3_obj;
if (!PyArg_ParseTuple(args, "OO", &list2_obj, &list3_obj))
return NULL;
double **list2;
double ***list3;
//Create C arrays from numpy objects:
int typenum = NPY_DOUBLE;
PyArray_Descr *descr;
descr = PyArray_DescrFromType(typenum);
npy_intp dims[3];
if (PyArray_AsCArray(&list2_obj, (void **)&list2, dims, 2, descr) < 0 || PyArray_AsCArray(&list3_obj, (void ***)&list3, dims, 3, descr) < 0) {
PyErr_SetString(PyExc_TypeError, "error converting to c array");
return NULL;
}
printf("2D: %f, 3D: %f.\n", list2[3][1], list3[1][0][2]);
}
这是我调用上述函数的 Python 代码:
import cmod, numpy
l2 = numpy.array([[1.0,2.0,3.0], [4.0,5.0,6.0], [7.0,8.0,9.0], [3.0, 5.0, 0.0]])
l3 = numpy.array([[2,7, 1], [6, 3, 9], [1, 10, 13], [4, 2, 6]]) # Line A
l3 = numpy.array([]) # Line B
l3 = numpy.array([[[2,7, 1, 11], [6, 3, 9, 12]],
[[1, 10, 13, 15], [4, 2, 6, 2]]])
cmod.func(l2, l3)
因此,如果我将 A 行和 B 行都注释掉,它会因总线错误而崩溃。如果 A 行存在,但 B 行被注释掉,则它运行正常,没有错误。如果 B 行存在,但 A 行被注释掉,它会打印正确的数字,但随后会出现 Seg 错误。最后,如果两行都存在,它还会打印正确的数字,然后是 Seg 错误。这到底是怎么回事?
编辑:好的。哇。所以我在 Python 中使用int,但在 C 中调用它们为double。这适用于一维和二维数组。但不是 3D。所以我将 l3 的 Python 定义更改为浮点数,现在一切正常(非常感谢 Bi Rico)。
但是现在,A 行和 B 行出现了更奇怪的行为!现在,如果两行都被注释掉,程序就可以工作了。如果 B 行存在,但 A 被注释掉,它可以工作,如果两者都未注释,则同上。但是,如果 A 行存在并且 B 被注释掉,我会再次收到那个奇妙的总线错误。我真的很想在以后避免这些,所以有人知道为什么 Python 变量的声明会产生这种影响吗?
编辑 2: 好吧,尽管这些错误很疯狂,但它们都是由于我传入的 3 维 numpy 数组。如果我只传入 1 维或 2 维数组,它的行为与预期一样,并且对其他 Python 变量的操作什么也不做。这让我相信问题出在 Python 的引用计数中。在 C 代码中,引用计数比 3-D 数组减少的要多,当该函数返回时,Python 会尝试清理对象,并尝试删除 NULL 指针。这只是我的猜测,我已经尝试Py_INCREF(); 我能想到的一切都无济于事。我想我只会使用二维数组并用 C 对其进行整形。
【问题讨论】:
-
你确定
(void **)是正确的,你不应该只是传入一个(void*)吗? -
我的 C 很烂,但是...如果第一次调用
PyArray_AsCArray成功,您在if中的表达式不是短路了吗?很可能第二个电话,即list3的电话,从来没有打过。 -
@seberg 我不确定
(void **)是否正确,但(void*)会导致总线错误。 @Jaime 不,该函数仅在失败时才返回负值,很可能是它调用的 malloc 失败。 -
@seberg 好的......现在我接受了 Bi Rico 的建议并尝试了 python 浮点数,单星和双星(或三星)似乎都可以正常工作。任何想法最好/正确?
-
得到以下错误:“错误:‘NPY_DOUBLE’未声明(在此函数中首次使用)”。
标签: python c pointers numpy python-c-extension