『Python CoolBook』C擴展庫_其一_用法講解

不依靠其餘工具,直接使用Python的擴展API來編寫一些簡單的C擴展模塊。html

本篇參考PythonCookbook第15節和Python核心編程完成,值得注意的是,Python2.X和Python3.X在擴展庫寫法上略有不一樣,咱們研究的是3.X寫法。python

1、源文件

Extest2.c編程

C函數本體

c文件頭必須包含"Python.h"頭,以調用接口函數bash

這裏面寫了兩個c函數,模塊名稱定爲Extestide

#include "Python.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int fac(int n)
{
	if (n<2) return(1);
	return (n)*fac(n-1);
}

char *reverse(char *s)
{
	register char t,
			 *p = s,
		     *q = (s + (strlen(s)-1));
    while (p < q)
    {
    	t = *p;
    	*p++ = *q;
    	*q-- = t;
    }
    return s;
}

Python API封裝

對這兩個函數採起C對Python API封裝,函數

  • 封裝函數爲靜態函數,輸入返回均爲PyObject*,且輸入須有一個self用於處理類對自身的引用,函數名須爲模塊名_c函數名格式(模塊名不是強制的)
    • static PyObject*
      模塊名稱_C函數名稱(PyObject *self, PyObject *args)
  • Python對象->C對象,用於C函數輸入,使用PyArg_ParseTuple爲元組輸入解析類型,另有PyArg_ParseTupleAndKeywords爲字典類型解析類型
    • PyArg_ParseTuple(args, "i", &num)  //PyObject參數,類型指定,C參數存放地址1,[C參數存放地址2……]
  • C對象->Python對象,用於C函數輸出,使用(PyObject*)Py_BuildValue進行類型轉換
    • (PyObject*)Py_BuildValue("ss", orig_str, \
      		dupe_str = reverse(strdup(orig_str)));  //類型指定,C參數1,[C參數2……]
  • return Python對象,因爲是個元組,因此上一步多少個C輸出都沒有關係
static PyObject*
Extest_fac(PyObject *self, PyObject *args)
{
	int num;
	if (!PyArg_ParseTuple(args, "i", &num))
		return NULL;
	return (PyObject*)Py_BuildValue("i", fac(num));
}

static PyObject*
Extest_doppel(PyObject *self, PyObject *args)
{
	char *orig_str;
	char *dupe_str;
	PyObject* retval;
	if (!PyArg_ParseTuple(args, "s", &orig_str)) return NULL;
	retval = (PyObject*)Py_BuildValue("ss", orig_str, \
		dupe_str = reverse(strdup(orig_str)));
	free(dupe_str);
	return retval;
}

首先,在擴展模塊中,你寫的函數都是像下面這樣的一個普通原型:工具

static PyObject *py_func(PyObject *self, PyObject *args) { ... } 

PyObject 是一個能表示任何Python對象的C數據類型。 在一個高級層面,一個擴展函數就是一個接受一個Python對象 (在 PyObject *args中)元組並返回一個新Python對象的C函數。 函數的 self 參數對於簡單的擴展函數沒有被使用到, 不過若是你想定義新的類或者是C中的對象類型的話就能派上用場了。好比若是擴展函數是一個類的一個方法, 那麼 self 就能引用那個實例了。測試

PyArg_ParseTuple() 函數被用來將Python中的值轉換成C中對應表示。 它接受一個指定輸入格式的格式化字符串做爲輸入,好比「i」表明整數,「d」表明雙精度浮點數, 一樣還有存放轉換後結果的C變量的地址。 若是輸入的值不匹配這個格式化字符串,就會拋出一個異常並返回一個NULL值。 經過檢查並返回NULL,一個合適的異常會在調用代碼中被拋出。ui

Py_BuildValue() 函數被用來根據C數據類型建立Python對象。 它一樣接受一個格式化字符串來指按期望類型。 在擴展函數中,它被用來返回結果給Python。 Py_BuildValue() 的一個特性是它能構建更加複雜的對象類型,好比元組和字典。 在 py_divide() 代碼中,一個例子演示了怎樣返回一個元組。不過,下面還有一些實例:spa

return Py_BuildValue("i", 34); // Return an integer return Py_BuildValue("d", 3.4); // Return a double return Py_BuildValue("s", "Hello"); // Null-terminated UTF-8 string return Py_BuildValue("(ii)", 3, 4); // Tuple (3, 4) 

庫信息記錄

 庫信息以及初始化信息

/* 記錄函數信息,{函數在Python中名稱,函數對應封裝,參數格式(此處表示參數以元組格式傳入)} */
static PyMethodDef
ExtestMethods[] = 
{
    {"fac", Extest_fac, METH_VARARGS},
    {"doppel", Extest_doppel, METH_VARARGS},
    {NULL, NULL},
};


/* Module structure */
static struct PyModuleDef Extestmodule = {
  PyModuleDef_HEAD_INIT,

  "Extest",           /* 庫名稱 */
  "A sample module",  /* Doc string (may be NULL) */
  -1,                 /* Size of per-interpreter state or -1 */
  ExtestMethods       /* 函數信息 */
};

/* Module initialization function */
PyMODINIT_FUNC
PyInit_Extest(void) {                      /* PyInit_庫名稱 */
  return PyModule_Create(&Extestmodule);   /* 參數爲Module structure名詞 */
}

在擴展模塊底部,你會發現一個函數表,好比本節中的 ExtestMethods 表。 這個表能夠列出C函數、Python中使用的名字、文檔字符串。 全部模塊都須要指定這個表,由於它在模塊初始化時要被使用到。

最後的函數 PyInit_Extest() 是模塊初始化函數,但該模塊第一次被導入時執行。 這個函數的主要工做是在解釋器中註冊模塊對象。

2、編譯文件setup.py

from distutils.core import setup, Extension

MOD = "Extest"
setup(name=MOD,  # 一個名字參數表示要編譯哪一個東西
    ext_modules=[ # 一個list對象列出編譯對象
      Extension(MOD, sources=['Extest2.c']) # 完整擴展名(可能含有.),源文件
   ])

3、測試調用

調用:

python setup.py build
python setup.py install

 測試:

相關文章
相關標籤/搜索