Python C-API: как использовать возвращенный словарь Python в Objective-C? - PullRequest
0 голосов
/ 02 июля 2018

Мой скрипт Python app.py имеет эту функцию, например:

def testmethod(x):
    return {"steps":["hello","world"]}

Я использую Python C-API в Objective-C следующим образом:

Py_Initialize();
PyObject *pName = PyUnicode_DecodeFSDefault("app");
PyObject* pModule = PyImport_Import(pName);
Py_DECREF(pName);

PyObject* myFunction = PyObject_GetAttrString(pModule,(char*)"testmethod");
PyObject* args = PyTuple_Pack(1,PyFloat_FromDouble(223.22));
PyObject* myResult = PyObject_CallObject(myFunction, args);
NSLog(@"Check for dict: %d",PyDict_Check(myResult)); // Prints true!!!!!!!!!!!! So definitely a dictionary

После этого я не совсем уверен, как я могу преобразовать myResult, чтобы получить свой словарь. Для двойников и строк мы обычно используем функции PyFloat_AsDouble и PyUnicode_AsUTF8String, PyBytes_AsString, но я не могу найти PyBLAHBLAH_AsDict или эквивалентный метод.

double result = PyFloat_AsDouble(myResult);
NSLog(@"Result: %f",result);*/

PyObject* pStrObj = PyUnicode_AsUTF8String(myResult);
char* zStr = PyBytes_AsString(pStrObj);
NSLog(@"String: %@",[NSString stringWithUTF8String:zStr]);

У меня нет большого опыта работы с Objective-C, поэтому, возможно, я не смотрю в нужное место.

Может кто-нибудь помочь мне понять, как я могу преобразовать PyObject в NSDictionary?

Я в основном ищу способ доступа к словарю на языке C (например, карту), который затем я могу использовать методы Objective-C для преобразования в NSDictionary. Подобно тому, как я использовал PyUnicode_AsUTF8String, PyBytes_AsString и stringWithUTF8String все вместе, чтобы получить NSString из строки Python. Ищете что-то похожее для словарей.

Я нашел способ достичь этого, но я не знаю, является ли это наилучшим из возможных способов сделать это. Я изменил свой скрипт на python, чтобы использовать json.dumps для возврата строкового представления словаря. Затем в myObjective C-коде я сначала получаю строку, а затем использую JSONObjectWithData, чтобы преобразовать строку json в NSDictionary. Пока это работает, но мне любопытно, есть ли лучший способ сделать это без необходимости возвращать строку вместо словаря из Python. Это больше похоже на обходное решение для меня. Вот что у меня сейчас:

app.py:

import json
def testmethod(x):
    return json.dumps({"steps":["hello","world"]})

Objective-C:

Py_Initialize();
PyObject *pName = PyUnicode_DecodeFSDefault("app");
PyObject* pModule = PyImport_Import(pName);
Py_DECREF(pName);

PyObject* myFunction = PyObject_GetAttrString(pModule,(char*)"testmethod");
PyObject* args = PyTuple_Pack(1,PyFloat_FromDouble(223.22));
PyObject* myResult = PyObject_CallObject(myFunction, args);

PyObject* pStrObj = PyUnicode_AsUTF8String(myResult);
char* zStr = PyBytes_AsString(pStrObj);
NSString *jsonStr = [NSString stringWithUTF8String:zStr];
NSLog(@"String: %@",jsonStr);

NSError *error;
NSData *data = [jsonStr dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *dictionary = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error];

NSLog(@"Dictionary: %@",dictionary);

1 Ответ

0 голосов
/ 02 июля 2018

Используйте PyDict_Next для итерации по парам ключ / значение словаря.

Вы также должны предоставить хэш-функции Python и функции равенства, если вы все еще собираетесь использовать PyObject* s. В противном случае вы должны попытаться привести их все к нативным типам.

(Это в псевдокоде, так как я на самом деле не знаю Objective-C, но использовал C-Api и Python)

NSObject* convert_py_object(PyObject* obj) {
    if (PyDict_Check(obj)) return convert_py_dict(obj);
    if (PyLong_Check(obj)) return convert_py_int(obj);
    if (PyUnicode_Check(obj)) return convert_py_str(obj);
    if (PyBytes_Check(obj)) return convert_py_bytes(obj);
    if (PyList_Check(obj) || PyTuple_Check(obj) || PyIter_Check(obj)) return convert_py_list(obj);
    throw error;
}

NSDictionary* convert_py_dict(PyObject* dict) {
    if (!PyDict_Check(dict)) throw error;
    NSDictionary* native_dict;
    PyObject *key;
    PyObject *value;
    Py_ssize_t pos = 0;

    while (PyDict_Next(self->dict, &pos, &key, &value)) {
        native_dict[convert_py_object(key)] = convert_py_object(value);
    }
    return native_dict;
}

Другая альтернатива - использовать API Python для доступа к значениям словаря. Поэтому итерируйте все значения с помощью PyDict_Next, PyDict_GetItemString для доступа к строковым элементам с простыми char * s и PyDict_GetItem для доступа к произвольному значению Python.

...