使用单个元组调用 PyObject_CallMethod 解压参数?

Calling PyObject_CallMethod with a single tuple unpacks arguments?

考虑以下几点:

PyObject* fmt = PyUnicode_FromString("{0!r}");
PyObject* tup = PyTuple_New(2);
PyTuple_SetItem(tup, 0, PyUnicode_FromString("hello"));
PyTuple_SetItem(tup, 1, PyUnicode_FromString("world"));
PyObject* formatted = PyObject_CallMethod(fmt, "format", "O", tup);
PyObject* bytes = PyUnicode_AsEncodedString(formatted, "UTF-8", "strict");
printf(PyBytes_AS_STRING(bytes));

我希望它像这样python代码:

>>> u'{0!r}'.format((u"hello", u"world"))
"(u'hello', u'world')"

但是我的输出很简单:

u'hello'

我可以想象它实际上是在调用这样的函数:

>>> u'{0!r}'.format(u"hello", u"world")
u'hello'

我在找什么:

  1. 为什么?
  2. 要获得预期输出,我需要做的最小改变是什么?

问题似乎与 Py_BuildValue 的工作方式有关(似乎被 PyObject_CallMethod). From the docs 使用(强调我的):

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.

这意味着不是将带有 tup 的格式字符串 "O" 构建为 args=(tup,) 并调用 fmt.format(*args)(扩展为 fmt.format(("hello", "world"))),而是构建args=tup,因此 fmt.format(*args) 扩展为 fmt.format("hello", "world"),如您所想。解决方案也在docs:

To force it to return a tuple of size 0 or one, parenthesize the format string.

所以,只需更改:

PyObject* formatted = PyObject_CallMethod(fmt, "format", "O", tup);

收件人:

PyObject* formatted = PyObject_CallMethod(fmt, "format", "(O)", tup);

你得到了 ('hello', 'world') 的期望输出。完整代码片段(使用 gcc thissnippet.c -I /usr/include/python3.4m/ -l python3.4m 编译):

#include <Python.h>
int main() {
    Py_Initialize();
    PyObject* fmt = PyUnicode_FromString("{0!r}");
    PyObject* tup = PyTuple_New(2);
    PyTuple_SetItem(tup, 0, PyUnicode_FromString("hello"));
    PyTuple_SetItem(tup, 1, PyUnicode_FromString("world"));
    PyObject* formatted = PyObject_CallMethod(fmt, "format", "(O)", tup);
    PyObject* bytes = PyUnicode_AsEncodedString(formatted, "UTF-8", "strict");
    printf(PyBytes_AS_STRING(bytes));
    Py_Finalize();
}