python 参数封装_扩展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:

intok;inti, j;longk, l;const char *s;intsize;

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)*/}

{intleft, 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_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)

{doublex0, y0, z0, x1, y1, z1;if (!PyArg_ParseTuple(args, "(ddd)(ddd)", &x0, &y0, &z0, &x1, &y1, &z1)) /*接受两个tuple类型的参数*/

{returnNULL;

}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");returnv;

}

Example:

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

Py_BuildValue("") None

Py_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(doublefval)

{

register PyFloatObject*op;if (free_list ==NULL) {if ((free_list = fill_free_list()) ==NULL)returnNULL;

}/*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;

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值