我正在尝试编写一个接受numpy数组作为输入的C扩展。一切正常,除了当我传入字符串作为参数时。
#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
#include "../../include/Python.h"
#include "../../include/arrayobject.h"
static PyObject *max(PyObject *self, PyObject *args)
{
PyArrayObject *arr;
long i, n, strides;
if (PyArg_ParseTuple(args, "O!", &PyArray_Type, &arr)){
/* Get some info about the data. */
n = PyArray_DIMS(arr)[0];
strides = PyArray_STRIDES(arr)[0];
void *data0 = PyArray_DATA(arr);
int typenum = PyArray_TYPE(arr);
if (typenum == NPY_DOUBLE){
double max = *(double *)data0;
for (i=0; i<n; ++i){
if (*(double *)data0 > max){
max = *(double *)data0;
}
data0 += strides;
}
return Py_BuildValue("d", max);
}
else if (typenum == NPY_LONG){
long max = *(long *)data0;
for (i=0; i<n; ++i){
if (*(long *)data0 > max){
max = *(long *)data0;
}
data0 += strides;
}
return Py_BuildValue("l", max);
}
else {
PyErr_Format(
PyExc_TypeError, "rInput should be a numpy array of numbers."
);
return NULL;
}
}
else{
PyErr_Format(
PyExc_TypeError, "rInput should be a numpy array of numbers."
);
return NULL;
}
}
static PyMethodDef DiffMethods[] =
{
{"max", max, METH_VARARGS, "Compute the maximum of a numpy array."},
{NULL, NULL, 0, NULL}
};
static struct PyModuleDef cModPyDem =
{PyModuleDef_HEAD_INIT, "_math_functions", "", -1, DiffMethods};
PyMODINIT_FUNC PyInit__math_functions(void)
{
import_array();
return PyModule_Create(&cModPyDem);
}
然后我运行这个 setup.py 脚本:
def configuration(parent_package=None, top_path=None):
import numpy
from numpy.distutils.misc_util import Configuration
config.add_extension('_math_functions', ['_math_functions.c'])
return config
if __name__ == "__main__":
from numpy.distutils.core import setup
setup(configuration=configuration)
使用以下命令:
python setup.py config --compiler=gnu99 build_ext --inplace
rm -rf build/
这很好用。该函数在大多数情况下都有效:
In [1]: import _math_functions as mf
In [2]: import numpy as np
In [3]: x = np.random.randint(-1e3, 1e3, size=100)
In [4]: np.max(x), mf.max(x)
Out[4]: (998, 998)
In [5]: x = np.random.rand(100)
In [6]: np.max(x), mf.max(x)
Out[6]: (0.9962604850115798, 0.9962604850115798)
它还可以处理不适当的输入,在某种程度上:
In [7]: x = np.array([1,2,"bob"])
In [8]: mf.max(x)
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-8-7ced17af9505> in <module>()
----> 1 mf.max(x)
Input should be a numpy array of numbers.
In [9]: mf.max("bob")
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-9-a656f60cf00d> in <module>()
----> 1 mf.max("bob")
Input should be a numpy array of numbers.
以下输入出现问题:
In [10]: x = np.array("Bob")
In [11]: mf.max(x)
Segmentation fault: 11
编辑:我尝试过的一些事情。 用:
PyArg_ParseTuple(args, "O", &arr)
相反,这仍然给出了一个 seg 错误。我还在每行之前放了printf("i")
(i = 1,2,...(,所以我确定段错误发生在PyArg_ParseTuple
。
我通读了文档并找到了"O&"
选项,但无法使其工作。欢迎就如何正确使用它提供任何建议。
我还浏览了这些相关帖子: PyArg_ParseTuple导致分段错误
PyArg_ParseTuple CApi 中的 SegFaults (不确定这个解决方案将如何应用...
在 Numpy 数组上调用PyArg_ParseTuple时崩溃
关于如何正确处理这个问题的任何线索?我想要的输出是引发的类型错误。
谢谢!
您是否尝试过添加调试语句来准确找出代码中发生分段错误的确切位置?
假设分段错误发生在这里:
if (PyArg_ParseTuple(args, "O!", &PyArray_Type, &arr)) {
尝试添加不同的解析机制(如"O"
(,以便不假定传递了PyArrayObject
实例;然后尝试使用生成的泛型PyObject*
并检查其类型(请参阅PyObject_TypeCheck
(,并根据类型控制程序的流程。从这里引发异常的方式在扩展文档中进行了解释,但我认为它是这样的:
PyErr_SetString(PyExc_TypeError, "Input should be a numpy array of numbers.");
return NULL;
天哪,这个问题与字符串完全无关。如果输入是零维的,PyArray_DIMS
和PyArray_STRIDES
返回NULL
,这就是问题所在。我放了更多的打印语句,该程序确实通过了PyArg_ParseTuple
.我真的是个傻瓜。这是一个完整的工作示例,我只是为这两个指针添加了检查。
#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
#include "../../include/Python.h"
#include "../../include/arrayobject.h"
static PyObject *max(PyObject *self, PyObject *args)
{
PyArrayObject *arr;
npy_int i, n, strides;
void *data0;
if (PyArg_ParseTuple(args, "O!", &PyArray_Type, &arr)){
// Check to make sure input isn't zero dimensional!
if ((PyArray_DIMS(arr) == NULL) || (PyArray_STRIDES(arr) == NULL)){
PyErr_Format(PyExc_TypeError,
"Input is zero-dimensional.");
return NULL;
}
// Useful information about the data.
int typenum = PyArray_TYPE(arr);
n = PyArray_DIMS(arr)[0];
strides = PyArray_STRIDES(arr)[0];
data0 = PyArray_DATA(arr);
if (typenum == NPY_DOUBLE){
double max = *(double *)data0;
for (i=0; i<n; ++i){
if (*(double *)data0 > max){
max = *(double *)data0;
}
data0 += strides;
}
return Py_BuildValue("d", max);
}
else if (typenum == NPY_LONG){
long max = *(long *)data0;
for (i=0; i<n; ++i){
if (*(long *)data0 > max){
max = *(long *)data0;
}
data0 += strides;
}
return Py_BuildValue("l", max);
}
else {
PyErr_Format(PyExc_TypeError,
"Input should be a numpy array of numbers.");
return NULL;
}
}
else{
PyErr_Format(PyExc_TypeError,
"Input should be a numpy array of numbers.");
return NULL;
}
}
static PyMethodDef DiffMethods[] =
{
{"max", max, METH_VARARGS, "Compute the maximum of a numpy array."},
{NULL, NULL, 0, NULL}
};
static struct PyModuleDef cModPyDem =
{PyModuleDef_HEAD_INIT, "_math_functions", "", -1, DiffMethods};
PyMODINIT_FUNC PyInit__math_functions(void)
{
import_array();
return PyModule_Create(&cModPyDem);
}
构建与以前相同。到目前为止,这通过了我所做的所有测试:
In [1]: import numpy as np
In [2]: import _math_functions as mf
In [3]: for i in range(1000):
...: for j in range(10):
...: x = np.random.rand((i+1)*100)
...: if ((np.max(x) - mf.max(x)) != 0):
...: print(i, j)
...: x = np.random.randint(-1e13*(i+1), 1e13*(i+1), size=1000)
...: if ((np.max(x) - mf.max(x)) !=0):
...: print(i, j)
...:
# Nothing prints, so np.max and mf.max are spitting out the same answer.
In [4]: mf.max("Bob")
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-4-bc67f3e1c10d> in <module>
----> 1 mf.max("Bob")
TypeError: Input should be a numpy array of numbers.
In [5]: mf.max(np.array(1))
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-5-1cb4380527fa> in <module>
----> 1 mf.max(np.array(1))
TypeError: Input is zero-dimensional.
In [6]: mf.max(np.array("Bob"))
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-6-47b1925b8c3c> in <module>
----> 1 mf.max(np.array("Bob"))
TypeError: Input is zero-dimensional.