您好,登錄后才能下訂單哦!
上篇博文是初用c/c++擴展Python,只是簡單的舉個例子,有興趣的可以去上篇博文里看看那個例子的代碼,代碼如下:
#include<Python.h> static PyObject *pr_isprime(PyObject *self,PyObject *args){ int n,num; if(!PyArg_ParseTuple(args,"i",&num)) return NULL; if(num<1){ return Py_BuildValue("i",0); } n=num-1; while(n>1){ if(num%n==0) return Py_BuildValue("i",0); n--; } return Py_BuildValue("i",1); } static PyMethodDef PrMethods[]={ {"isPrime",pr_isprime,METH_VARARGS,"check if an input numbe is prime or not."}, {NULL,NULL,0,NULL} }; void initpr(void){ (void) Py_InitModule("pr",PrMethods); }
這兩天花時間簡單的研究了一下那個代碼,其中最關鍵的是Python.h這頭文件,我們可以看看這個頭文件的源代碼。(用的是Python2.7.12,Ubuntu16.04 LTS,Python.h在/usr/include/python2.7/里)
為了節省篇幅,特意將源代碼中注釋給刪掉,不便之處敬請諒解。
#ifndef Py_PYTHON_H #define Py_PYTHON_H #include "patchlevel.h" #include "pyconfig.h" #include "pymacconfig.h" #ifndef WITH_CYCLE_GC #define WITH_CYCLE_GC 1 #endif #include <limits.h> #ifndef UCHAR_MAX #error "Something's broken. UCHAR_MAX should be defined in limits.h." #endif #if UCHAR_MAX != 255 #error "Python's source code assumes C's unsigned char is an 8-bit type." #endif #if defined(__sgi) && defined(WITH_THREAD) && !defined(_SGI_MP_SOURCE) #define _SGI_MP_SOURCE #endif #include <stdio.h> #ifndef NULL # error "Python.h requires that stdio.h define NULL." #endif #include <string.h> #ifdef HAVE_ERRNO_H #include <errno.h> #endif #include <stdlib.h> #ifdef HAVE_UNISTD_H #include <unistd.h> #endif #ifdef HAVE_STDDEF_H #include <stddef.h> #endif #include <assert.h> #include "pyport.h" #ifndef DL_IMPORT /* declarations for DLL import/export */ #define DL_IMPORT(RTYPE) RTYPE #endif #ifndef DL_EXPORT /* declarations for DLL import/export */ #define DL_EXPORT(RTYPE) RTYPE #endif #if defined(Py_DEBUG) && defined(WITH_PYMALLOC) && !defined(PYMALLOC_DEBUG) #define PYMALLOC_DEBUG #endif #if defined(PYMALLOC_DEBUG) && !defined(WITH_PYMALLOC) #error "PYMALLOC_DEBUG requires WITH_PYMALLOC" #endif #include "pymath.h" #include "pymem.h" #include "object.h" #include "objimpl.h" #include "pydebug.h" #include "unicodeobject.h" #include "intobject.h" #include "boolobject.h" #include "longobject.h" #include "floatobject.h" #ifndef WITHOUT_COMPLEX #include "complexobject.h" #endif #include "rangeobject.h" #include "stringobject.h" #include "memoryobject.h" #include "bufferobject.h" #include "bytesobject.h" #include "bytearrayobject.h" #include "tupleobject.h" #include "listobject.h" #include "dictobject.h" #include "enumobject.h" #include "setobject.h" #include "methodobject.h" #include "moduleobject.h" #include "funcobject.h" #include "classobject.h" #include "fileobject.h" #include "cobject.h" #include "pycapsule.h" #include "traceback.h" #include "sliceobject.h" #include "cellobject.h" #include "iterobject.h" #include "genobject.h" #include "descrobject.h" #include "warnings.h" #include "weakrefobject.h" #include "codecs.h" #include "pyerrors.h" #include "pystate.h" #include "pyarena.h" #include "modsupport.h" #include "pythonrun.h" #include "ceval.h" #include "sysmodule.h" #include "intrcheck.h" #include "import.h" #include "abstract.h" #include "compile.h" #include "eval.h" #include "pyctype.h" #include "pystrtod.h" #include "pystrcmp.h" #include "dtoa.h" PyAPI_FUNC(PyObject*) _Py_Mangle(PyObject *p, PyObject *name); #define PyArg_GetInt(v, a) PyArg_Parse((v), "i", (a)) #define PyArg_NoArgs(v) PyArg_Parse(v, "") #define Py_CHARMASK(c) ((unsigned char)((c) & 0xff)) #include "pyfpe.h" #define Py_single_input 256 #define Py_file_input 257 #define Py_eval_input 258 #ifdef HAVE_PTH #include <pth.h> #endif #define PyDoc_VAR(name) static char name[] #define PyDoc_STRVAR(name,str) PyDoc_VAR(name) = PyDoc_STR(str) #ifdef WITH_DOC_STRINGS #define PyDoc_STR(str) str #else #define PyDoc_STR(str) "" #endif #endif /* !Py_PYTHON_H */
代碼沒幾句,就是一堆頭文件,而且在Python.h文件里沒有找到 PyArg_ParseTuple()、Py_BuildValue()、PyMethodDef、PrMethods、METH_VARARGS、Py_InitModule這些變量或者函數。說實話,我第一看也納悶呀,怎么Python.h文件里沒有這些變量或者函數呢?所以很快就想到一定是在包含的頭文件里的某些文件里,這么多,怎么找呀?我是寫腳本程序找的,腳本程序很簡單,在此就不貼代碼了,幾秒鐘就找到了這些函數或者變量是在哪個文件里定義的。下面來一一介紹這幾個變量或者函數吧,有不正確的地方,歡迎批評指正。
(1)PyArg_ParseTuple()
該函數定義在/usr/include/python2.7/modsupport.h里。這個文件里有一段文字解釋——”Module support interface“,也就是模塊支持接口,這個文件里應該就是定義了對外擴展的接口。這個函數的原型是:
PyAPI_FUNC(int) PyArg_ParseTuple(PyObject *, const char *, ...)
該函數的功能是將Python對象C/C++類型數據,如果轉換失敗,返回0
第一個參數:包含從Python傳遞到C函數的參數列表的元組對象
第二個參數:是格式參數,必須是字符串,已經預定義好了的,零個或多個“格式單位”組成。一個格式單元描述一個Python對象。比如例子中的‘i'表示將Python整數對象轉換為純C語言的 int類型。
其余參數:其余參數必須是其類型由格式字符串確定的變量的地址,可以是多個地址。上面例子用的就 是num的地址&num表示的就是num的地址,&是取值運算符
一些常見的格式參數:
"s":將Python字符串或Unicode對象轉換為C里面字符串的指針,即 Python中string o或者Unicode 對象轉換為C語言里 char *
“s#”:“s”上的這個變體存儲到兩個C變量中,第一個是指向字符串的指針,第二個是它的長度。在這種情況下,Python字符串可能包含嵌入的空字節。如果可以進行這種轉換,Unicode對象將傳回指向對象的默認編碼字符串版本的指針。所有其他讀緩沖區兼容對象傳回對原始內部數據表示的引用。即(字符串,Unicode或任何讀取緩沖區兼容對象)→[char *,int]。
“z”:像“s”,但Python對象也可以是None,在這種情況下,C指針設置為NULL。即string或None)→[char *]
“z#”:(字符串或無或任何讀緩沖區兼容對象)→[char *,int]。
“u”:將Python Unicode對象轉換為C指針,指向16位Unicode(UTF-16)數據的空終止緩沖區。即(Unicode對象)→[Py_UNICODE *] 。
“u#”:這個變量“u”存儲到兩個C變量中,第一個是指向Unicode數據緩沖區的指針,第二個是它的長度。(Unicode對象)→[Py_UNICODE *,int]。
“es”:“s”上的此變體用于將Unicode和可轉換為Unicode的對象編碼為字符緩沖區。它只適用于沒有嵌入NULL字節的編碼數據。變量讀取一個變量并存儲到兩個C變量中,第一個是指向編碼名稱字符串(編碼)的指針,第二個是指向字符緩沖區的指針的指針,即(字符串,Unicode對象或字符緩沖區兼容對象)→[const char * encoding,char ** buffer]。
“es#”:類似”es",只是第三個指向整數的指針(* buffer_length,緩沖區長度)。編碼名稱必須映射到注冊的編×××。如果設置為NULL,則使用默認編碼。即(字符串,Unicode對象或字符緩沖區兼容對象)→[const char * encoding,char ** buffer,int * buffer_length]。
“h”:將Python整數轉換為C short int,即(integer)→[short int]
“i”:將Python整數轉換為純C int。即(integer)→[int]
“l”:將Python整數轉換為C long int,即(integer)→[long int]
“c”:將一個Python字符(表示為長度為1的字符串)轉換為C char,即(長度為1的字符串)→[char]
“f”:將Python浮點數轉換為C浮點,即(float)→[float]
“d”:將Python浮點數轉換為C double,即(float)→[double]
“D”:將Python復雜數字轉換為C Py_complex結構,即(復合物)→[Py_complex]
“O”:將Python對象(無任何轉換)存儲在C對象指針中。 C程序因此接收被傳遞的實際對象。對象的引用計數不增加。存儲的指針不為NULL。(object)→[PyObject *]
“O!":將Python對象存儲在C對象指針。這類似于“O”,但有兩個C參數:第一個是Python類型對象的地址,第二個是存儲對象指針的C變量(類型PyObject *)的地址。如果Python對象沒有必需的類型,則會引發TypeError。(object)→[typeobject,PyObject *]
“O&”:通過轉換器函數將Python對象轉換為C變量。這需要兩個參數:第一個是一個函數,第二個是C變量(任意類型)的地址,(object)→[converter,anything]
“S”:像“O”,但要求Python對象是一個字符串對象。如果對象不是字符串對象,則引發TypeError。 C變量也可以聲明為PyObject *。(string)→[PyStringObject *]
“u”:像“O”,但要求Python對象是一個Unicode對象。如果對象不是Unicode對象,則引發TypeError。 C變量也可以聲明為PyObject *。(Unicode字符串)→[PyUnicodeObject *]
“t#”:類似“s#”,但接受任何實現只讀緩沖區接口的對象。 char *變量被設置為指向緩沖區的第一個字節,int被設置為緩沖區的長度。只接受單段緩沖對象;對所有其他類型引發TypeError。(只讀字符緩沖區)→[char *,int]
“w”:類似于“s”,但接受實現讀寫緩沖器接口的任何對象。調用者必須通過其他方式確定緩沖區的長度,或者使用“w#”。只接受單段緩沖對象;對所有其他類型引發TypeError。(讀寫字符緩沖區)→[char *]
“w#”:類似“s#”,但接受任何實現讀寫緩沖區接口的對象。 char *變量被設置為指向緩沖區的第一個字節,int被設置為緩沖區的長度。只接受單段緩沖對象;對所有其他類型引發TypeError。(讀寫字符緩沖區)→[char *,int]
“items”:對象必須是Python序列,其長度是項目中的格式單位數。 C參數必須對應于各個格式單元initem。 可以嵌套序列的格式單元。(tuple)→[matching-items]
如果對Python源碼稍微有點了解的話,PyObject 、PyStringObject 、PyUnicodeObject等都是Python源碼里用C語言為Python定義的類型,有興趣的可以看看《Python源碼解析》這本書,里面都有介紹。
另外還有一些其他字符在格式字符串中有意義,
“|”:表示Python參數列表中的其余參數是可選的。 對應于可選參數的C變量應該被初始化為它們的默認值 - 當沒有指定可選參數時,PyArg_ParseTuple()不觸及相應的C變量的內容。
“:”:格式單元列表在這里結束; 冒號之后的字符串用作錯誤消息中的函數(“PyArg_ParseTuple()”引發的異常的“關聯值”)。
“;”:格式單元列表在這里結束; 冒號之后的字符串用作錯誤消息,而不是默認錯誤消息。 顯然,“:”和“;” 互相排斥。
(2)Py_BuildValue()
該函數也是定義在/usr/include/python2.7/modsupport.h里,原型如下:
PyAPI_FUNC(PyObject *) Py_BuildValue(const char *, ...)
它的功能與PyArg_ParseTuple()正好相反,它是將C類型的數據結構轉換成Python對象。
它第一個參數是格式參數,同PyArg_ParseTuple()格式參數一樣,其余參數就是一些C類型的數據咯。
看幾個例子吧,對它的理解會有幫助。
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))
(3)Py_InitModule()
該函數也是定義在/usr/include/python2.7/modsupport.h里,返回一個指針指向剛創建的模塊對象,看名字也知道是初始化新建模塊的。函數原型:
#define Py_InitModule(name, methods) \ Py_InitModule4(name, methods, (char *)NULL, (PyObject *)NULL, \ PYTHON_API_VERSION)
是宏定義,接受兩個參數,第一個參數為字符串,表示模塊的名稱;第二個參數是一個PyMethodDef的結構體數組,表示該模塊都具有哪些方法。
PyMethodDef結構體有四個字段。
* 第一個是一個字符串,表示在Python中對應的方法的名稱;
* 第二個是對應的C代碼的函數;
* 第三個是一個標致位,表示該Python方法是否需要參數,METH_NOARGS表示不需要參數,METH_VARARGS表示需要參數,這個參數在/usr/include/python2.7/methodobject.h有定義;
* 第四個是一個字符串,它是該方法的__doc__屬性,這個不是必須的,可以為NULL。
其源代碼如下:
struct PyMethodDef { const char *ml_name; /* The name of the built-in function/method */ PyCFunction ml_meth; /* The C function that implements it */ int ml_flags; /* Combination of METH_xxx flags, which mostly describe the args expected by the C func */ const char *ml_doc; /* The __doc__ attribute, or NULL */ }; typedef struct PyMethodDef PyMethodDef;
是在/usr/include/python2.7/methodobject.h中定義的。
PyMethodDef結構體數組最后以 {NULL, NULL, 0, NULL}結尾。(感覺好像不是必須的,但是通常都這么做那我們也這么做吧)不正之處,歡迎批評指正!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。