zoukankan      html  css  js  c++  java
  • 扩展Python模块系列(三)----参数解析与结果封装

    在上一节中,通过一个简单的例子介绍了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 in
    H ==> unsigned short int
    i ==> int
    I ==> unsigned int
    l ==> long int
    k ==> unsigned long
    L ==> PY_LONG_LONG,将Python整数转为C的long long,有些平台可能不支持;
    K ==> unsigned PY_LONG_LONG
    f ==> 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("")                        None
    Py_BuildValue("i", 123)                  123
    Py_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;
    }

    作者: 建木
    出处: http://www.cnblogs.com/jianmu/
    本文版权归作者和博客园所有,如有转载,需注明出处。

  • 相关阅读:
    SharePoint Framework (SPFx) 开发入门教程
    SharePoint 2013 Designer 入门教程
    SharePoint 2013 开发教程
    SharePoint 2013 入门教程
    SharePoint Online 部署SPFx Web部件
    SharePoint Online SPFx Web部件绑定数据
    SharePoint Online 创建SPFx客户端Web部件
    SharePoint Online 配置框架(SPFx)开发环境
    SharePoint Online 创建应用程序目录
    SharePoint Online 启用 IRM
  • 原文地址:https://www.cnblogs.com/jianmu/p/7367566.html
Copyright © 2011-2022 走看看