博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
扩展Python模块系列(三)----参数解析与结果封装
阅读量:6814 次
发布时间:2019-06-26

本文共 5218 字,大约阅读时间需要 17 分钟。

在上一节中,通过一个简单的例子介绍了C语言扩展Python内建模块的整体流程,从本节开始讲开始深入讨论一些细节问题,在细节讨论中从始至终都会涉及【引用计数】的问题。首先讨论C语言封装的Python函数的参数解析与函数结果返回的封装。

参数解析

最常用的接口是

int PyArg_ParseTuple(PyObject *arg, char *format, ...);

arg是一个tuple object,从python传递给C函数;format参数必须是一个字符串,通常每个字符代表一种类型;剩下的参数是与format相对应的各个变量的地址,返回值是一个整型,解析成功返回1,解析出错返回0.

函数接收的Python Object参数是borrowed reference,所以不需要增加引用计数。

Example: 

int ok;int i, j;long k, l;const char *s;int size;ok = PyArg_ParseTuple(args, ""); /* 无参数 */    /* Python call: f() */
ok = PyArg_ParseTuple(args, "s", &s); /* 参数为一个字符串 */    /* Possible Python call: f('whoops!') */
ok = PyArg_ParseTuple(args, "lls", &k, &l, &s); /* 参数为两个长整型与一个字符串 */    /* Possible Python call: f(1, 2, 'three') */
{    const char *file;    const char *mode = "r";    int bufsize = 0;    ok = PyArg_ParseTuple(args, "s|si", &file, &mode, &bufsize);    /* 参数至少有一个字符串,可以另外有一个字符串或整型 */    /* Possible Python calls:       f('spam')       f('spam', 'w')       f('spam', 'wb', 100000) */}
{    int left, top, right, bottom, h, v;    ok = PyArg_ParseTuple(args, "((ii)(ii))(ii)",             &left, &top, &right, &bottom, &h, &v);    /* 参数为两个元组,第一个元组有两个元组元素,每个元组为两个整数构成 */    /* Possible Python call:       f(((0, 0), (400, 300)), (10, 10)) */}
PyObject* p;ok = PyArg_ParseTuple(args, "O", &p); /* 参数为一个PyObject对象,可以表示Python中的任意类型 */ /*Possible Python call: f((1,2))*/

Python C API中提供的常见形式字符串如下所示(没有全部列出,其余的可以参考Python2.7文档):

s  ==> const char*, 将Python字符串转为字符指针;s# ==> const cahr*, Py_ssize_t, 将Python字符串转为字符指针以及字符创长度;b ==> unsigned char, Python非负整数转为C unsigned char;B ==> unsigned char, Python整数转为C unsigned char;h ==> short inH ==> unsigned short inti ==> intI ==> unsigned intl ==> long intk ==> unsigned longL ==> PY_LONG_LONG,将Python整数转为C的long long,有些平台可能不支持;K ==> unsigned PY_LONG_LONGf ==> float, 将Python的浮点数转为C的float。Pyhton中仅有double类型,所以这里会有精度丢失。d ==> double, 将Python浮点数转为C的double,无精度丢失O==> PyObject*, 将Python对象保存在PyObject*中,这里object的引用计数不会增加;(items)==> tuple/*其他字符*/| |后面的参数是可选的, PyArg_ParseTuple中需要提供缺省参数的默认值;:字符串参数列表在:结束,:后面的表示对函数的解释说明;;;后面的用于错误说明,替代默认的错误信息
static PyObject* distance(PyObject* self, PyObject* args){    double x0, y0, z0, x1, y1, z1;    if (!PyArg_ParseTuple(args, "(ddd)(ddd)", &x0, &y0, &z0, &x1, &y1, &z1))    /*接受两个tuple类型的参数*/    {        return NULL;    }    return PyFloat_FromDouble(sqrt((x0 - x1) * (x0 - x1) + (y0 - y1) * (y0 - y1) + (z0 - z1) * (z0 - z1)));}

 

结果返回

与参数解析函数PyArg_ParseTuple相对应的是:

PyObject *Py_BuildValue(char *format, ...);

format同样指明了各个参数列表的各个参数的类型,但是传递给该函数的参数不能是指针,这里有PyArg_ParseTuple不同,只传递值即可。另外一个重要的区别是,Py_BuildValue返回的是PyObject*, 引用计数自动为1,如果传递给该函数一个PyObject*参数,比如以下代码,此时p的引用参数会increamented by one.

PyObject*  p = PyFloat_FromDouble(1.0);Py_BuildValue('O', p);

在Python源码中: Py_BuildValue会调用static PyObject* do_mkvalue(const char **p_format, va_list *p_va, int flags),这里对于‘O'的处理如下:

case 'N':        case 'S':        case 'O':        if (**p_format == '&') {            typedef PyObject *(*converter)(void *);            converter func = va_arg(*p_va, converter);            void *arg = va_arg(*p_va, void *);            ++*p_format;            return (*func)(arg);        }        else {            PyObject *v;            v = va_arg(*p_va, PyObject *);            if (v != NULL) {                if (*(*p_format - 1) != 'N')                    Py_INCREF(v);    /*如果format不是'N',那么引用计数会增加1,如果format是'N',引用计数不变*/            }            else if (!PyErr_Occurred())                /* If a NULL was passed                 * because a call that should                 * have constructed a value                 * failed, that's OK, and we                 * pass the error on; but if                 * no error occurred it's not                 * clear that the caller knew                 * what she was doing. */                PyErr_SetString(PyExc_SystemError,                    "NULL object passed to Py_BuildValue");            return v;        }

 

Example:

左边是函数调用形式,右边是返回的Python value:

Py_BuildValue("")                        NonePy_BuildValue("i", 123)                  123Py_BuildValue("iii", 123, 456, 789)      (123, 456, 789)Py_BuildValue("s", "hello")              'hello'Py_BuildValue("ss", "hello", "world")    ('hello', 'world')Py_BuildValue("s#", "hello", 4)          'hell'Py_BuildValue("()")                      ()Py_BuildValue("(i)", 123)                (123,)Py_BuildValue("(ii)", 123, 456)          (123, 456)Py_BuildValue("(i,i)", 123, 456)         (123, 456)Py_BuildValue("[i,i]", 123, 456)         [123, 456]Py_BuildValue("{s:i,s:i}",              "abc", 123, "def", 456)    {
'abc': 123, 'def': 456}Py_BuildValue("((ii)(ii)) (ii)", 1, 2, 3, 4, 5, 6) (((1, 2), (3, 4)), (5, 6))

除了使用Py_BuildValue函数返回Python对象之外,还可以调用每个类型所提供的封装函数,比如我们之前的test模块中,distance函数需要返回一个Python float对象,那么可以调用floatobject提供的PyFloat_FromDouble:

PyObject *PyFloat_FromDouble(double fval){    register PyFloatObject *op;    if (free_list == NULL) {        if ((free_list = fill_free_list()) == NULL)            return NULL;    }    /* Inline PyObject_New */    op = free_list;    free_list = (PyFloatObject *)Py_TYPE(op);    (void)PyObject_INIT(op, &PyFloat_Type);  /*初始化引用计数*/    op->ob_fval = fval;    return (PyObject *) op;}

 

转载于:https://www.cnblogs.com/jianmu/p/7367566.html

你可能感兴趣的文章
装了一款系统优化工具,如何从Mac上卸载MacBooster 7?
查看>>
使用符号表调试release程序
查看>>
Delphi 设置系统默认打印机
查看>>
AliOS Things网络适配框架 - SAL
查看>>
数组 将一个数组的元素和另一个素组的元素相加,然后赋给第三个数组
查看>>
Python常用模块汇总
查看>>
sa提开放系统下的虚拟新贵Virtualbox权技巧之xp_regwrite替换sethc.exe
查看>>
SpringBoot开发案例之整合Dubbo提供者(一)
查看>>
变态的程序
查看>>
腾讯抄你肿么办 ?
查看>>
java多线程的Fork/Join
查看>>
ftp 服务器的配置
查看>>
JavaScript的浏览器兼容性问题小结。
查看>>
Oracle Hint的用法
查看>>
Postfix邮件系统
查看>>
《编写可读代码的艺术》读书文摘--第一部分 表面层次的改进
查看>>
使用Nodejs创建基本的网站 Microblog--《Node.js开发指南》 3
查看>>
网管工作是否值得做下去?
查看>>
神行者PD10-adb push逃脱ro权限
查看>>
JPA(四)之实体关系一对一
查看>>