问题
I have a function foo
in a Python Extension Module that should return a tuple of ints to Python. This can be easily done using Py_BuildValue
:
static PyObject*
foo(PyObject* self, PyObject* args)
{
int a = 0;
int b = 0;
/* calculations and stuff */
PyObject* out = Py_BuildValue("(iii)", a, b, a+b);
Py_INCREF(out);
return out;
}
Instead of Py_BuildValue
, I want to use PyTuple_Pack
, which ensures that the return value is indeed a tuple.
The Python C API documentation says that PyTuple_Pack(3, a, b, a+b)
is equivalent to Py_BuildValue("(iii)", a, b, a+b)
. Both functions return a new reference of type PyPbject*
.
Hence, given the code above,
static PyObject*
foo(PyObject* self, PyObject* args)
{
/* ... */
PyObject* out = PyTuple_Pack(3, a, b, a+b);
Py_INCREF(out);
return out;
}
should do the trick, which is does not. Instead I get a segfault. What am I missing here?
回答1:
The difference is:
Py_BuildValue("(ii)", a, b)
expectsa
andb
to be simple C-int values.PyTuple_Pack(2, a, b)
expectsa
andb
to be alreadyPyObject
s (and not C-ints).
The documentation says:
The tuple values are initialized to the subsequent n C arguments pointing to Python objects.
PyTuple_Pack(2, a, b)
is equivalent toPy_BuildValue("(OO)", a, b)
.
In order to use PyTuple_Pack
you need to convert the int-values to Python-Integers first.
It is simpler to use Py_BuildValue()
. If you parenthesize your format string in Py_BuildValue, the result will be a tuple:
Py_BuildValue()
does not always build a tuple. It builds a tuple only if its format string contains two or more format units. If the format string is empty, it returns None; if it contains exactly one format unit, it returns whatever object is described by that format unit. To force it to return a tuple of size 0 or one, parenthesize the format string.
That means: there is nothing to worry about if you construct a tuple from at least two elements:
Py_BuildValue("ii", a, b) # returns a tuple
Py_BuildValue("(ii)", a, b) # returns a tuple
It is different if there is only one element:
Py_BuildValue("i", a) # returns an integer
# parenthesized:
Py_BuildValue("(i)", a) # returns a tuple with an integer
or no elements at all:
Py_BuildValue("") # returns None
# parenthesized:
Py_BuildValue("()") # returns an empty tuple.
So just make sure there are parenthesizes in the format string and the returned value will be a tuple.
来源:https://stackoverflow.com/questions/52179761/pytuple-pack-segfault