速度与激情的碰撞, C语言解析Python字典代码, 就是如此任性!

这里我们简单说一些背景,就是突然想写一点用C去写Python库的东西,下面的这些方法和用法大部分参考官方介绍

Python中字典

从参数中获取

从参数中获取的情况一般有两种,一般是我们如果明确了传入类型就是字典,并且需要知道需要解析的key,那么可以采用如下的方式进行解析:

static PyObject *my_func(PyObject* self, PyObject* args, PyObject* keyds)

{

static char* kwlist[] = {"age","name","level",NULL};

int age;

char *name;

int level;

if(!PyArg_ParseTupleAndKeywords(args,keyds,"isi", kwlist, &age,&name,&level))

{

return NULL;

}

/* 其他的工作 */

}

其中kwlist是需要获取的字典中的key列表,但是这样的话,我们的参数就变成了三个,因此需要修改一下声明:

{

"my_func"

,

(PyCFunction)my_func,

METH_VARARGS | METH_KEYWORDS,

"my_func"

},

在函数绑定的时候进行了一次类型转换。

还有一种情况是,我们只是获取字典,用于后面的解析工作,也不确定字典中的keylist,那么可以使用如下的方式来读:

PyObject *dict;

if(!PyArg_ParseTuple(args, "O!", &PyDict_Type,&dict))

//if(!PyArg_ParseTuple(args, "O", &dict))

{

return NULL;

}

两种方式均可以获取字典,其中O和O!的区别,后续再说。

解析一个dict

获取dict中的key

我们知道Python中的dict的keys其实是一个list,同样,在C语言中也是一样的,我们通过下面这个函数获取keys_list:

PyAPI_FUNC(PyObject *) PyDict_Keys(PyObject *mp);

该函数的返回值的同样也是一个PyObject*的类型,也就是我们常说的key_list。如果想便利keys_list中的key,这可以使用list的函数进行遍历,函数原型如下:

PyAPI_FUNC(PyObject *) PyList_GetItem(PyObject *, Py_ssize_t);

其中两个参数分别是list以及在list中的位置。简单一点的遍历的例子如下:

PyObject* py_keys = PyDict_Keys(dict);

for(int i =0;i

{

//do something

//...

}

通过key获取字典中的值

接下来就是通过key获取字典中的值,通常会借用下面的函数

PyAPI_FUNC(PyObject *) PyDict_GetItem(PyObject *mp, PyObject *key);

PyAPI_FUNC(PyObject *) PyDict_GetItemString(PyObject *dp, const char *key);

通过函数原型很容易知道,第一个API是通过PyObject获取,第二个是通过字符串key获取。

因为C语言不同于Python的中一切皆为对象,这里需要判断一下val的类型,以及完成对应类型的转换:

字符串类型

Python2中是采用PyString_Check函数进行甄别的,判断是否为字符串,通过PyString_AsString函数完成从PyObject* 到char*的转换。

在Python3中是使用PyUnicode_Check进行是否为字符串的判断,通过PyUnicode_AsUTF8可以完成从PyObject* 到char*的转换。

整型

在Python2中,有两个文件均提供了判断的函数:

1. intobject.h提供了PyInt_Check(op)

2. longobject.h提供了PyLong_Check(op)

那么肯定是两种转换的接口:

1. intobject.h提供了PyAPI_FUNC(int) _PyInt_AsInt(PyObject *)

2. intobject.h提供了PyAPI_FUNC(long) PyInt_AsLong(PyObject *)

3. longobject.h提供了PyAPI_FUNC(long) PyLong_AsLong(PyObject *);

4. longobject.h提供了 PyAPI_FUNC(PY_LONG_LONG) PyLong_AsLongLong(PyObject *)

简单的例子如下:

int val = PyInt_AsLong(py_val);

在Python3中则没有这么多的转换函数,已经没有PyInt这一些函数了,因此只有Python2中一半的函数:

PyLong_Check(op)

longobject.h:PyAPI_FUNC(long) PyLong_AsLong(PyObject *);

longobject.h:PyAPI_FUNC(unsigned long) PyLong_AsUnsignedLong(PyObject *)

字典类型

同样的,在Python只能够字典的value也可以是字典,因此可以通过PyDict_Check来判断这个值得类型是不是字典。从而进行更深入的解析。

下面是一个简单的把dict读入到一个buffer中例子,其实也可以构建一个cpp中的类似Python的字典的类型。

static int dict2str(PyObject* dict , char* buffer, int buf_size)

{

int n_pos = 0;

int buf_cost_len = 0;

int buf_unused_len = buf_size;

if(dict == NULL)

{

//printf("the dict is null\n");

return -1;

}

if(NULL == buffer || buf_size

{

//printf("the buffer is null\n");

return -1;

}

PyObject* py_keys = PyDict_Keys(dict);

/* debug */

#define DEBUG_UINT(val) \

do{ \

printf("[%s][%s][%d][%s][%u]\n",__FILE__,__FUNCTION__,__LINE__,#val,val); \

}while(0)

DEBUG_UINT(PyDict_Size(dict));

DEBUG_UINT(PyList_Size(py_keys));

DEBUG_UINT(PY_MAJOR_VERSION);

for(int i =0;i

{

PyObject* py_key = PyList_GetItem(py_keys,i);

PyObject* py_val = PyDict_GetItem(dict, py_key);

//还可以使用PyDict_GetItemString,此时key为char*

#if PY_MAJOR_VERSION == 2

if(!PyString_Check(py_key))

{

continue;

}

char *key = PyString_AsString(py_key);

if (PyString_Check(py_val)) {

char *val = PyString_AsString(py_val);

n_pos = snprintf(buffer+buf_cost_len ,buf_unused_len,"key[%s],val[%s]|",key,val);

buf_unused_len -= n_pos;

buf_cost_len += n_pos;

continue;

}

if (PyInt_Check(py_val)) {

int val = PyInt_AsLong(py_val);

n_pos = snprintf(buffer+buf_cost_len ,buf_unused_len,"key[%s],val[%d]|",key,val);

buf_unused_len -= n_pos;

buf_cost_len += n_pos;

continue;

}

if (PyDict_Check(py_val)) {

//TODO: go on;

n_pos = snprintf(buffer+buf_cost_len ,buf_unused_len,"key[%s],val[%d]|",key,"{");

buf_unused_len -= n_pos;

buf_cost_len += n_pos;

int ret = dict2str(buffer+buf_cost_len,py_val,buf_unused_len);

if(ret >0)

{

buf_unused_len -= ret;

buf_cost_len += ret;

}

n_pos = snprintf(buffer+buf_cost_len ,buf_unused_len,"}|");

buf_unused_len -= n_pos;

buf_cost_len += n_pos;

continue;

}

#else

if(!PyUnicode_Check(py_key))

{

//printf("Error:[%d]the key is PyUnicode_Check error\n",__LINE__);

continue;

}

char * key = PyUnicode_AsUTF8(py_key);

//printf("key[%s]\n",key);

if(PyUnicode_Check(py_val))

{

//string

char *val = PyUnicode_AsUTF8(py_val);

n_pos = snprintf(buffer+buf_cost_len ,buf_unused_len,"key[%s],val[%s]|",key,val);

buf_unused_len -= n_pos;

buf_cost_len += n_pos;

continue;

}

if (PyLong_Check(py_val)) {

/* 新版中我没有找到转换为int 的支持,基本上都是long */

long val = PyLong_AsLong(py_val);

n_pos = snprintf(buffer+buf_cost_len ,buf_unused_len,"key[%s],val[%ld]|",key,val);

buf_unused_len -= n_pos;

buf_cost_len += n_pos;

continue;

}

if (PyDict_Check(py_val)) {

//TODO: go on;

n_pos = snprintf(buffer+buf_cost_len ,buf_unused_len,"key[%s],val[%s",key,"{");

buf_unused_len -= n_pos;

buf_cost_len += n_pos;

int ret = dict2str(py_val,buffer+buf_cost_len,buf_unused_len);

if(ret >0)

{

buf_unused_len -= ret;

buf_cost_len += ret;

}

n_pos = snprintf(buffer+buf_cost_len ,buf_unused_len,"}]|");

buf_unused_len -= n_pos;

buf_cost_len += n_pos;

continue;

}

#endif

}

return buf_cost_len;

}

返回一个字典

通过上面的一些例子,我们不难发现,其实,在Python中,完成以后返回给Python用户使用者的还是一个类的对象指针,同样我们可以轻松的通过Py_BuildValue构建一个对象进行返回。

PyAPI_FUNC(PyObject *) Py_BuildValue(const char *fmt, ...)

其中fmt为格式,举个例子,如果要返回一个str的类型,可以如下:

return Py_BuildValue("s","OK");

当然我们还有更为简单,明确的:

return PyUnicode_FromString("ok");

不过,看起来Py_BuildValue更为通用一些,那么可想而知,如果想返回一个字典,就可以如下使用:

PyObject* ret_str = Py_BuildValue("","result",buffer);

return ret_str;

这里虽然比较简单,但是不可控的因素比较多,通常我可能更传统一点的使用下面的方式来返回一个字典:

PyObject* ret_dict = PyDict_New();

// return Py_BuildValue("","result","ok","result_code",0,"return_string",buffer );

ret = PyDict_SetItemString(ret_dict, "result",Py_BuildValue("s","ok"));

ret = PyDict_SetItemString(ret_dict, "result_code",Py_BuildValue("i",0));

ret = PyDict_SetItemString(ret_dict, "return_string",Py_BuildValue("s",buffer));

return ret_dict;

异曲同工,但是下面的更为灵活,不是么?至少不需要提前获知需要使用的类型都有什么。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,014评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,796评论 3 386
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,484评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,830评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,946评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,114评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,182评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,927评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,369评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,678评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,832评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,533评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,166评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,885评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,128评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,659评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,738评论 2 351

推荐阅读更多精彩内容