2012-09-25 20 views
11

我正在寫一個Python C-Extension而不使用Cython。C數組到PyArray

我想在C中分配一個雙數組,在內部函數中使用它(恰好在Fortran中)並返回它。我想指出的是,C-Fortran的接口完全在C.

static PyObject * 
Py_drecur(PyObject *self, PyObject *args) 
{ 
    // INPUT 
    int n; 
    int ipoly; 
    double al; 
    double be; 

    if (!PyArg_ParseTuple(args, "iidd", &n, &ipoly, &al, &be)) 
    return NULL; 

    // OUTPUT 
    int nd = 1; 
    npy_intp dims[] = {n}; 
    double a[n]; 
    double b[n]; 
    int ierr; 

    drecur_(n, ipoly, al, be, a, b, ierr); 

    // Create PyArray 
    PyObject* alpha = PyArray_SimpleNewFromData(nd, dims, NPY_DOUBLE, a); 
    PyObject* beta = PyArray_SimpleNewFromData(nd, dims, NPY_DOUBLE, b); 

    Py_INCREF(alpha); 
    Py_INCREF(beta); 

    return Py_BuildValue("OO", alpha, beta); 
} 

我調試的代碼,我得到一個分段錯誤,當我嘗試創建阿爾法出來的。到那裏一切正常。函數drecur_有效,如果刪除它,我會得到相同的問題。

現在,圍繞C數據定義PyArray的標準方式是什麼?我找到了文檔,但沒有很好的例子。另外,內存泄漏呢?返回前INCREF是否正確,以便保留Alpha和Beta實例?什麼時候不再需要它們的釋放呢?

編輯 我終於在NumPy cookbook找到了正確的方法。

static PyObject * 
Py_drecur(PyObject *self, PyObject *args) 
{ 
    // INPUT 
    int n; 
    int ipoly; 
    double al; 
    double be; 
    double *a, *b; 
    PyArrayObject *alpha, *beta; 

    if (!PyArg_ParseTuple(args, "iidd", &n, &ipoly, &al, &be)) 
    return NULL; 

    // OUTPUT 
    int nd = 1; 
    int dims[2]; 
    dims[0] = n; 
    alpha = (PyArrayObject*) PyArray_FromDims(nd, dims, NPY_DOUBLE); 
    beta = (PyArrayObject*) PyArray_FromDims(nd, dims, NPY_DOUBLE); 
    a = pyvector_to_Carrayptrs(alpha); 
    b = pyvector_to_Carrayptrs(beta); 
    int ierr; 

    drecur_(n, ipoly, al, be, a, b, ierr); 

    return Py_BuildValue("OO", alpha, beta); 
} 

double *pyvector_to_Carrayptrs(PyArrayObject *arrayin) { 
    int n=arrayin->dimensions[0]; 
    return (double *) arrayin->data; /* pointer to arrayin data as double */ 
} 

請隨時對此發表評論,並感謝您的答案。

回答

1

一個問題可能是您的數組(a,b)必須持續至少與包含它的numpy數組一樣長。你已經在本地作用域創建了你的數組,所以當你離開這個方法時它們將被銷燬。

嘗試讓python分配數組(例如使用PyArray_SimpleNew),將您的內容複製到它並傳遞一個指針。如果建立反對增強是一種選擇,您可能還想使用boost::python來處理這些細節。

3

因此,看起來可疑的第一件事是,您的數組ab是在該函數的本地範圍。這意味着在返回之後,您將獲得非法內存訪問權限。

所以,你應該用

double *a = malloc(n*sizeof(double)); 

然後,你需要確保內存是後來由你所創建的對象釋放聲明數組。 看到這個報價文檔:

的PyObject PyArray_SimpleNewFromData(INT第二,npy_intp變暗,INT typenum,無效*數據)

有時候,你想換其他地方分配到ndarray內存用於下游使用。這個例程使得直接做到這一點。前三個參數與PyArray_SimpleNew中的參數相同,最後一個參數是一個指向ndarray應該使用的連續內存塊的指針,因爲它的數據緩衝區將以C風格連續的方式進行解釋。將返回對ndarray的新引用,但ndarray將不擁有其數據。當這個ndarray被釋放時,指針不會被釋放。

您應確保在返回的數組存在時提供的內存不會被釋放。處理這個問題的最簡單方法是如果數據來自另一個引用計數的Python對象。在傳入指針後,應該增加此對象的引用計數,並且返回的ndarray的基本成員應該指向擁有數據的Python對象。然後,當ndarray被釋放時,基本成員將被適當地DECREF。如果您希望在ndarray解除分配後立即釋放內存,則只需在返回的ndarray上設置OWNDATA標誌即可。

對於你的第二個問題Py_INCREF(alpha);一般只需要如果你打算保持一個全局變量或類成員參考。 但是,既然你只是包裝一個功能,你不必這樣做。 不幸的是,它可能是功能PyArray_SimpleNewFromData不會將參考計數器設置爲1,如果這將是您將不得不將其增加到1的情況,我希望這是可以理解的;)。