How to return a list of ints in Python C API extension with PyList?
Asked Answered
I

1

5

I am building a Python extension (.pyd) using Visual Studio 2015 C++ project and Python 2.7 32bit.

This is my .cpp file:

#include <Python.h>

static PyObject* GetTwoInts(PyObject* self, PyObject* args)
{
    srand(time(NULL));
    int random1 = rand() % 10;
    int random2 = rand() % 10;

    PyObject * python_val = Py_BuildValue("ii", random1, random2);
    return python_val;
}

static PyObject* GetListTwoInts(PyObject* self, PyObject* args)
{
    srand(time(NULL));
    int random1 = rand() % 10;
    int random2 = rand() % 10;

    PyObject *val1 = PyInt_FromLong(random1);
    PyObject *val2 = PyInt_FromLong(random2);

    PyObject *result = PyList_New(2);
    PyList_SetItem(result, 0, val1);
    PyList_SetItem(result, 1, val2);

    PyObject * python_val = Py_BuildValue("ii", result);
    return python_val;
}

PyMODINIT_FUNC initUtils(void)
{
    static PyMethodDef methods[] = {
        { "GetTwoInts", GetTwoInts, METH_NOARGS,
        "Get two C ints as a Python tuple with two random numbers" },

        { "GetListTwoInts", GetListTwoInts, METH_NOARGS,
        "Get a list with two random numbers" },
        { NULL, NULL, 0, NULL },

    };

    PyObject *m = Py_InitModule("Utils", methods);
}

This is the Python source code using the compiled extension:

import sys
import Utils
print help(Utils)
print Utils.GetTwoInts()
print Utils.GetListTwoInts()

This is the output:

(4, 2)
(91213912, 91213912)

So, the Py_BuildValue("ii", random1, random2); gave me a proper tuple with two random ints, just as expected. However, returning a list in the GetListTwoInts method gives me invalid numbers (looks like a reference value or a pointer?).

What should I do to return a list of real values instead in the GetListTwoInts method?

Interleave answered 3/6, 2018 at 17:13 Comment(2)
Why are you not returning result? That is your list.Throes
@IgnacioVazquez-Abrams, thanks that was it. I just had to return the result.Interleave
T
13

You can change the format of Py_BuildValue so that it builds a list instead of a tuple. Just use "[ii]" instead of "ii" as first argument:

static PyObject* GetListTwoInts(PyObject* self, PyObject* args)
{
    srand(time(NULL));
    int random1 = rand() % 10;
    int random2 = rand() % 10;

    PyObject * python_val = Py_BuildValue("[ii]", random1, random2);
    return python_val;
}

You can use PyList_New and PyList_SetItem if you want to create a list of dynamic size.

static PyObject* GetList(PyObject* self, PyObject* args)
{
    srand(time(NULL));
    int const N = 10;
    PyObject* python_val = PyList_New(N);
    for (int i = 0; i < N; ++i)
    {
        int r = rand() % 10;
        PyObject* python_int = Py_BuildValue("i", r);
        PyList_SetItem(python_val, i, python_int);
    }
    return python_val;
}

The problem with the PyList version in your question is that you are using Py_BuildValue("ii", result) on the list. This tries to create a tuple of two integers, where the first value is the result pointer casted to an integer.

Tropopause answered 4/6, 2018 at 14:58 Comment(3)
thanks. However, I would like to return a list that contains an arbitrary number of items so supplying each item as an argument to Py_BuildValue is not an option. Is it possible to return the whole list without getting each of its item as an argument? Something like Py_BuildValue([i], [1,2,4..n])?Interleave
@AlexTereshenkov I edited my answer and added an example that uses PyList_New and PyList_SetItem to create a list with dynamic size.Tropopause
or using PyList_Append, this could be : PyObject *list = PyList_New(0); iterate through the array/list; PyList_Append(list, PyFloat_FromDouble(element)); return list;Electrometer

© 2022 - 2024 — McMap. All rights reserved.