我想在c中写一个python对象,它包含一个numpy向量(实际上是两个,但在这个最小的例子中只有一个)。
首先,我只想创建一个带有numpy数组的对象,看看我可以在循环中为所有数组元素添加一个。但即使这样也会导致奇怪的(可重现的)行为和段错误。
以下是发生的事情(REPL):
from rletest import Rle
r = Rle(range(1, 10))
r.runs
# array([1., 2., 3., 4., 5., 6., 7., 8., 9.])
r.add()
r.runs
# array([ 2., 3., 4., 5., 6., 7., 8., 9., 10.])
r.add()
fish: 'python' terminated by signal SIGSEGV (Address boundary error)
我可以像这样解决这个问题:
... r2 = r.add()
>>> r3 = r2.add()
>>> r4 = r3.add()
>>> r4.runs
array([ 4., 5., 6., 7., 8., 9., 10., 11., 12.])
我怀疑这与引用计数有关?我是一个完整的新手,无能为力。任何帮助赞赏。这个功能(或我对它的使用)可能是罪犯:
static PyObject * Rle_add(Rle* self)
{
int N = (int)PyArray_DIM(self->runs, 0);
double *x = (double*)PyArray_DATA(self->runs);
add(x, N);
return (PyObject *)self;
}
它为了速度而就地更改数组,但这不应该导致错误,正如您从上面的解决方法中看到的那样。
下面是我的代码,因此这是可重复的。
我有以下文件:
test_rle.c:
#include <Python.h>
#include <numpy/arrayobject.h>
#include "structmember.h"
#include "add.h"
/* static PyObject *add(PyObject *self, PyObject *args); */
typedef struct {
PyObject_HEAD
PyObject *runs; /* run lengths */
} Rle;
static void
Rle_dealloc(Rle* self)
{
Py_XDECREF(self->runs);
Py_TYPE(self)->tp_free((PyObject*)self);
}
static PyObject *
Rle_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
printf("New was called! New was called! New was called!\n");
Rle *self;
PyObject *rs, *vs;
import_array();
if (!PyArg_ParseTuple(args, "O", &rs))
return NULL;
PyObject *runs = PyArray_FROM_OTF(rs, NPY_DOUBLE, NPY_IN_ARRAY);
self = (Rle *)type->tp_alloc(type, 0);
if (self != NULL) {
self->runs = runs;
if (self->runs == NULL) {
Py_DECREF(self);
return NULL;
}
}
printf("Reference count after new: %d\n", Py_REFCNT(self));
return (PyObject *)self;
}
static int
Rle_init(Rle *self, PyObject *args, PyObject *kwds)
{
printf("Init was called! Init was called! Init was called!\n");
printf("Reference count after init: %d\n", Py_REFCNT(self));
return 0;
}
static PyMemberDef Rle_members[] = {
{"runs", T_OBJECT_EX, offsetof(Rle, runs), 0,
"Run lengths"},
{NULL} /* Sentinel */
};
static PyObject * Rle_add(Rle* self)
{
int N = (int)PyArray_DIM(self->runs, 0);
double *x = (double*)PyArray_DATA(self->runs);
add(x, N);
return (PyObject *)self;
}
static PyMethodDef Rle_methods[] = {
{"add", (PyCFunction)Rle_add, METH_NOARGS,
"Add 1 to the runs"
},
{NULL} /* Sentinel */
};
static PyTypeObject RleType = {
PyVarObject_HEAD_INIT(NULL, 0)
"rle.Rle", /* tp_name */
sizeof(Rle), /* tp_basicsize */
0, /* tp_itemsize */
(destructor)Rle_dealloc, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_reserved */
0, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
0, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT |
Py_TPFLAGS_BASETYPE, /* tp_flags */
"Rle objects", /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
Rle_methods, /* tp_methods */
Rle_members, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
(initproc)Rle_init, /* tp_init */
0, /* tp_alloc */
Rle_new, /* tp_new */
};
static PyModuleDef rletestmodule = {
PyModuleDef_HEAD_INIT,
"rletest",
"Example module that creates an extension type.",
-1,
NULL, NULL, NULL, NULL, NULL
};
PyMODINIT_FUNC
PyInit_rletest(void)
{
PyObject* m;
if (PyType_Ready(&RleType) < 0)
return NULL;
m = PyModule_Create(&rletestmodule);
if (m == NULL)
return NULL;
Py_INCREF(&RleType);
PyModule_AddObject(m, "Rle", (PyObject *)&RleType);
return m;
}
setup.py:
from distutils.core import setup, Extension
setup(name="rle", version="1.0",
ext_modules=[Extension("rletest", ["test_rle.c", "add.c"])])
add.h
void add(double *x, int N);
add.c:
#include <stdio.h>
void add(double *x, int N) {
int n;
for (n = 0; n < N; n++) {
x[n] += 1.0;
}
}
将它们全部放在同一个文件夹中并运行
python setup.py build_ext --inplace
建立它。
答案 0 :(得分:2)
return (PyObject *)self;
您现在有self
的额外引用,因此您需要incref
它。因为它不是你没有分配返回值所以Python立即decrefs
它和r
不再存在。
(你的workround避免了直接的decref
,但我很惊讶它之后会导致分段错误。)