擴展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;
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM