本篇承接 Part1Part1 中我們已經成功的寫一個 C 的函式 並讓 Python 程式去使用他

不過其中並沒有參數的傳遞和回傳 也就是我們最需要的部分還沒有完成 現在就來看看該怎麼做~

在這裡我們來寫一個費氏數列的函式做示範

$$ \left{\begin{array}{11} f{(0)} &= 0 \ f{(1)} &= 1 \ f{(n)} &= f{(n-1)} + f_{(n-2)} \end{array} \right. $$

wikibooks 的教學中, 這也是第二個例子 我自己也習慣在 Hello World 之後測試費式數列, 感覺好像可以寫出費氏數列函式, 就可以做到很多事情

首先我們再寫一個 C 程式如下:

fib.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <Python.h>

int _fib(int n) {
    if (n < 2)
        return n;
    else
        return _fib(n-1) + _fib(n-2);
}

static PyObject* fib(PyObject* self, PyObject* args) {
    int n;
    if (!PyArg_ParseTuple(args, "i", &n))
        return NULL;
    return Py_BuildValue("i", _fib(n));
}

static PyMethodDef FibMethods[] = {
    {"fib", fib, METH_VARARGS, "Calculate the Fibonacci numbers."},
    {NULL, NULL, 0, NULL}
};

PyMODINIT_FUNC initfib(void) {
    (void) Py_InitModule("fib", FibMethods);
}

首先看 int _fib(int n), 這是求費氏數列的函式, 負責實際的求解運算 非常簡單的遞迴演算法, 就不多做說明了

1
2
3
4
5
6
static PyObject* fib(PyObject* self, PyObject* args) {
    int n;
    if (!PyArg_ParseTuple(args, "i", &n))
        return NULL;
    return Py_BuildValue("i", _fib(n));
}

這部分是本篇的重點, 他作為和 python 的接口, 接受參數並回傳結果 這函式長得和之前的 hello 很像, 不同的地方是 我們不再忽略 args, 也不直接回傳 None 了

來看看如何獲得傳進來的參數: 關鍵就在於 PyArg_ParseTuple() 這個函式 args => 存放參數的變數 “i” => 讀入一個 integer &n => 把讀進來的那個整數, 存到 n 的位置 就這麼簡單!

如果我們要讀入一個字串呢?

1
2
char str[128];
PyArg_ParseTuple(args, "s", str);

第一個參數是字串, 第二個參數是整數的話:

1
2
3
char str[128];
int n;
PyArg_ParseTuple(args, "si", str, &n);

詳細的使用可以參考這份文件: http://docs.python.org/dev/c-api/arg.html

回傳結果呢?

1
 return Py_BuildValue("i", _fib(n));

因為我們必須回傳的是一個 pythonObject*, 而不能直接把一個 int 回傳 所以要先把 C 的整數轉換成包成 pythonObject 的整數, 而使用的就是 Py_BuildValue 這個函式 至於詳細的使用方法可以參考上面提過的那份文件, 在此我就只就範例來說明

return Py_BuildValue(“i”, _fib(n)); 這行的意義就是: 我們把 _fib(n)的結果轉換為 pythonObject 之後回傳

現在完成了這份 fib.c, 請把他按照上一篇的說明編譯 然後就可以在 Python 中使用他了!

test_fib.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import fib, time

def c_fib(n):
    return fib.fib(n)

def py_fib(n):
    if n < 2:
        return n
    return py_fib(n-1) + py_fib(n-2)

def test_fib():
    n = int(input('N: '))
    ts = time.clock()
    print 'computing with C: '
    print c_fib(n)
    print "%.2gs" % (time.clock() - ts)

    ts = time.clock()
    print 'computing with Python: '
    print py_fib(n)
    print "%.2gs" %(time.clock() - ts)


if __name__ == '__main__':
    test_fib()

在這份測試中, 我另外寫了一個 python 版本的求費氏數列的函式 用以比較速度

在我的電腦上測試的結果, 使用 C 的版本約比 Python 的版本快了 100 倍左右 可見這個做法對於解決效能瓶頸的障礙是會有幫助的!

Reference

Python C API wikibook: python programming stackoverflow

License

This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License.

見人言某語言優雅有感

有些語言可能可以說是好用.靈巧.在programmer能力值高的情況下能產出好的程式但卻很難被歸類為 “優雅”##比如說這麼一個語言混淆應屬於物件的 method 或獨立的 function甚至狂熱試圖把一切都當成物件然後鼓勵一堆人寫出 `3.times do` 這種念起來很順 …… Continue reading

[Note]Setup SSH on Fedora in VirtualBox

Published on January 30, 2015