How to write C extension with Python.h

Jianshu.com does not support MathJax, to view the math equations, go to https://www.zybuluo.com/kailaix/note/486830.


In this short article, I'd like to introduce the minimal effort to write C extension for Python.

Why do we have to write C extension for Python? In most times, Python does well on the calculations we want to make. However, sometimes we'd like to speed up our program by improving the most time-consuming and CPU-consuming parts. Switching to C programming might be a remedy but coding in pure C will be miserable. In such a situation, writing C extension is definitely a good idea.

There are might ways to write C extension. For example, swig, Cython and so on are great tools for such a purpose. But we will focus on the primitive "Python.h way" in this article to achieve the goal.

The Problem

We will introduce the method by solving the following problem:

Consider the following differential equation:
$$\frac{dy}{dt} = y$$
with initial condition $y(0)=1$. Calculate $y(1)$ and $y(2)$.

The answer is obvious as we can solve the problem analyticall: $y(t)=e^t$. But to illustrate, we will use backward Euler's method to solve it numerically. The method reads:

$$y_0=1, y_{n+1}=\frac{1}{1-\Delta t}y_n$$

that is, the step size is $\Delta t$. Let $\Delta t=\frac{1}{N}$, then $y_N \approx y(1)$; also when $\Delta t=\frac{2}{N}$, $y_N \approx y(2)$.

The Struction of C Source Code

Like MATLAB mex function, Python.h provides us with power APIs to write C extension. In a typical C source code to be integrated into Python program, there are four parts:

  • C functions you want to integrate into Python program.
  • A converter to coordinate the data structures of C and Python.
  • A table mapping the names of your functions as Python developers see them to C functions inside the extension module.
  • An initialization function.
C functions

The first part consists of primitive C functions like

double _value_at_one(int n){
    double dt = 1.0/n;
    double f = 1;
    for(int i=0;i<n;i++)
    {
        f = 1.0/(1-dt)*f;
    }
    return f;
}

double _value_at_two(int n){
    double dt = 2.0/n;
    double f = 1;
    for(int i=0;i<n;i++)
    {
        f = 1.0/(1-dt)*f;
    }
    return f;
}

This is pure C and we do not have to worry about interations between C and Python.

converter
static PyObject *
value_at_one(PyObject *self, PyObject *args)
{
    int n;
    if (!PyArg_ParseTuple(args,"i",&n)){
        return NULL;
    }
    return Py_BuildValue("d",_value_at_one(n));
}

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

In this part, we wrap the C functions in a "converter". There are three kinds of converters, which differs only in the arguments they take: besides self arguments, functions with no arguments,functions with tuple arguments and functions with key word arguments.

For the second case, the standard API to parse the arguments is PyArg_ParseTuple function.

Py_BuildValue constructs a Python data structure from C data structure.

mapping table

Mapping table is a array of structures that assemble all the functions in a single container.

static PyMethodDef calculate_methods[] = {
    /* The cast of the function is necessary since PyCFunction values
     * only take two PyObject* parameters, and keywdarg_parrot() takes
     * three.
     */
    {"value_at_one", (PyCFunction)value_at_one, METH_VARARGS,
     "This is a demo"},
     {"value_at_two", (PyCFunction)value_at_two, METH_VARARGS,
     "value_at_two demo"},
    {NULL, NULL, 0, NULL}   /* sentinel */
};

Every struction consists of the function name you want to expose to the Python developers, a pointer to the function constructed in the last step, a flag to identify the type of the function and a help string. The last structure must be something like the example above, marking the end of the table.

initialization function

It is a routine to initialize the function at the end of the source code.

PyMODINIT_FUNC
initdiff_calulate(void)
{
  /* Create the module and add the functions */
  (void)Py_InitModule("diff_calulate", calculate_methods);
}

diff_calculate will then be the package name. The initialization function name is init<PACKAGE_NAME>.

one more thing: header files

Python.h should be included.

If you run gcc example.c -o examle in the command line, you will get a fatal error saying that Python.h is not found. This is because the .h file is not in the default search path of clang. You should look for the right version of Python.h. For me, it is /usr/include/python2.7. But by using the setup.py method, we do not have to worry about it.

Glue C Source Code to Python

The Python package distutils provides us with a great way to setup the Python module with nearly no effort.

Continuing the example above, we can create a file setup.py in the same directory:

from distutils.core import setup, Extension

module = Extension('diff_calculate',sources=['example.c'])

setup(
    ext_modules = [module]
    )

Note that the first arguments of Extension class must be the same as the package name. You can also add name=...,version=... arguments to setup(...) as you like. Then in the command line, run python setup.py build. You will obtain a build directory, in which you can find diff_calculate.so-- this is the package you can directly put into use.

Use the package

To use the package, import will do:

>>> import diff_calculate
>>> diff_calculate.value_at_one(100)
2.7319990264290435
>>> diff_calculate.value_at_two(100)
7.540366073866224

Summary

Writing C extension for Python is easy. In practice, to rewrite the pure C code to a Python extension, we can just modify an example code because most of the steps are very similar. A great tutorial on Python C extension is hosted on tutorialspoint.

Enjoy Python and use Python to do scientific computing!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末月匣,一起剝皮案震驚了整個濱河市明郭,隨后出現(xiàn)的幾起案子判沟,更是在濱河造成了極大的恐慌览芳,老刑警劉巖泌射,帶你破解...
    沈念sama閱讀 206,378評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異绰垂,居然都是意外死亡窥突,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評論 2 382
  • 文/潘曉璐 我一進店門矾麻,熙熙樓的掌柜王于貴愁眉苦臉地迎上來纱耻,“玉大人,你說我怎么就攤上這事险耀∨” “怎么了?”我有些...
    開封第一講書人閱讀 152,702評論 0 342
  • 文/不壞的土叔 我叫張陵甩牺,是天一觀的道長蘑志。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么急但? 我笑而不...
    開封第一講書人閱讀 55,259評論 1 279
  • 正文 為了忘掉前任澎媒,我火速辦了婚禮,結(jié)果婚禮上波桩,老公的妹妹穿的比我還像新娘戒努。我一直安慰自己,他們只是感情好镐躲,可當我...
    茶點故事閱讀 64,263評論 5 371
  • 文/花漫 我一把揭開白布储玫。 她就那樣靜靜地躺著,像睡著了一般萤皂。 火紅的嫁衣襯著肌膚如雪撒穷。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,036評論 1 285
  • 那天裆熙,我揣著相機與錄音端礼,去河邊找鬼。 笑死入录,一個胖子當著我的面吹牛蛤奥,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播纷跛,決...
    沈念sama閱讀 38,349評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼喻括,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了贫奠?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,979評論 0 259
  • 序言:老撾萬榮一對情侶失蹤望蜡,失蹤者是張志新(化名)和其女友劉穎唤崭,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體脖律,經(jīng)...
    沈念sama閱讀 43,469評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡谢肾,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,938評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了小泉。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片芦疏。...
    茶點故事閱讀 38,059評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖微姊,靈堂內(nèi)的尸體忽然破棺而出酸茴,到底是詐尸還是另有隱情,我是刑警寧澤兢交,帶...
    沈念sama閱讀 33,703評論 4 323
  • 正文 年R本政府宣布薪捍,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏酪穿。R本人自食惡果不足惜凳干,卻給世界環(huán)境...
    茶點故事閱讀 39,257評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望被济。 院中可真熱鬧救赐,春花似錦、人聲如沸只磷。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽喳瓣。三九已至馋贤,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間畏陕,已是汗流浹背配乓。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留惠毁,地道東北人犹芹。 一個月前我還...
    沈念sama閱讀 45,501評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像鞠绰,于是被迫代替她去往敵國和親腰埂。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,792評論 2 345

推薦閱讀更多精彩內(nèi)容