Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Passing a Python list to C function using the Python/C API

Tags:

python

c

I've recently started using the Python/C API to build modules for Python using C code. I've been trying to pass a Python list of numbers to a C function without success:

asdf_module.c

#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <Python.h>

int _asdf(int low, int high, double *pr)
    // ...

    for (i = 0; i < range; i++)
    {
        printf("pr[%d] = %f\n", i, pr[i]);
    }

    // ...

    return 0;
}

static PyObject*
asdf(PyObject* self, PyObject* args)
{
    int low, high;
    double *pr;
    // maybe something about PyObject *pr ?

    if (!PyArg_ParseTuple(args, "iiO", &low, &high, &pr))
        return NULL;

    // how to pass list pr to _asdf?

    return Py_BuildValue("i", _asdf(low, high, pr));
}

static PyMethodDef AsdfMethods[] =
{
     {"asdf", asdf, METH_VARARGS, "..."},
     {NULL, NULL, 0, NULL}
};

PyMODINIT_FUNC
initasdf(void)
{
    (void) Py_InitModule("asdf", AsdfMethods);
}

Building the module with setup.py :

from distutils.core import setup, Extension

module1 = Extension('asdf', sources = ['asdf_module.c'])

setup (name = 'asdf',
        version = '1.0',
        description = 'This is a demo package',
        ext_modules = [module1])

Using the module in test_module.py :

import asdf

print asdf.asdf(-2, 3, [0.7, 0.0, 0.1, 0.0, 0.0, 0.2])

However, what I got as an output is :

pr[0] = 0.000000

pr[1] = 0.000000

pr[2] = 0.000000

pr[3] = 0.000000

pr[4] = 0.000000

pr[5] = -nan

Also, instead of _asdf returning 0, how can it return an array of n values (where n is a fixed number)?

like image 622
M. D. Avatar asked Aug 21 '16 10:08

M. D.


1 Answers

This example will show you how to

  1. Get the length of the list
  2. Allocate storage for an array of double
  3. Get each element of the list
  4. Convert each element to a double
  5. Store each converted element in the array

Here is the code:

#include "Python.h"

int _asdf(double pr[], int length) {
    for (int index = 0; index < length; index++)
        printf("pr[%d] = %f\n", index, pr[index]);
    return 0;
}

static PyObject *asdf(PyObject *self, PyObject *args)
{
    PyObject *float_list;
    int pr_length;
    double *pr;

    if (!PyArg_ParseTuple(args, "O", &float_list))
        return NULL;
    pr_length = PyObject_Length(float_list);
    if (pr_length < 0)
        return NULL;
    pr = (double *) malloc(sizeof(double *) * pr_length);
    if (pr == NULL)
        return NULL;
    for (int index = 0; index < pr_length; index++) {
        PyObject *item;
        item = PyList_GetItem(float_list, index);
        if (!PyFloat_Check(item))
            pr[index] = 0.0;
        pr[index] = PyFloat_AsDouble(item);
    }
    return Py_BuildValue("i", _asdf(pr, pr_length));
}

NOTE: White space and braces removed to keep code from scrolling.

Test program

import asdf
print asdf.asdf([0.7, 0.0, 0.1, 0.0, 0.0, 0.2])

Output

pr[0] = 0.700000
pr[1] = 0.000000
pr[2] = 0.100000
pr[3] = 0.000000
pr[4] = 0.000000
pr[5] = 0.200000
0

Addressing Concerns About Memory Leaks

People seem to be worried about the code not freeing the memory. This is just enough code to show how to convert the list to double. I wanted the code to be able to fit in the text box without scrolling, so it is not production code.

like image 158
David Cullen Avatar answered Oct 04 '22 03:10

David Cullen