溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

再探C/C++擴(kuò)展Python

發(fā)布時(shí)間:2020-06-17 18:33:40 來(lái)源:網(wǎng)絡(luò) 閱讀:3595 作者:孤雁不獨(dú)飛 欄目:編程語(yǔ)言

    上篇博文是初用c/c++擴(kuò)展Python,只是簡(jiǎn)單的舉個(gè)例子,有興趣的可以去上篇博文里看看那個(gè)例子的代碼,代碼如下:

#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);
}

這兩天花時(shí)間簡(jiǎn)單的研究了一下那個(gè)代碼,其中最關(guān)鍵的是Python.h這頭文件,我們可以看看這個(gè)頭文件的源代碼。(用的是Python2.7.12,Ubuntu16.04 LTS,Python.h在/usr/include/python2.7/里)

為了節(jié)省篇幅,特意將源代碼中注釋給刪掉,不便之處敬請(qǐng)諒解。

#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 */

代碼沒(méi)幾句,就是一堆頭文件,而且在Python.h文件里沒(méi)有找到 PyArg_ParseTuple()、Py_BuildValue()、PyMethodDef、PrMethods、METH_VARARGS、Py_InitModule這些變量或者函數(shù)。說(shuō)實(shí)話,我第一看也納悶呀,怎么Python.h文件里沒(méi)有這些變量或者函數(shù)呢?所以很快就想到一定是在包含的頭文件里的某些文件里,這么多,怎么找呀?我是寫(xiě)腳本程序找的,腳本程序很簡(jiǎn)單,在此就不貼代碼了,幾秒鐘就找到了這些函數(shù)或者變量是在哪個(gè)文件里定義的。下面來(lái)一一介紹這幾個(gè)變量或者函數(shù)吧,有不正確的地方,歡迎批評(píng)指正。

(1)PyArg_ParseTuple()

        該函數(shù)定義在/usr/include/python2.7/modsupport.h里。這個(gè)文件里有一段文字解釋——”Module support interface“,也就是模塊支持接口,這個(gè)文件里應(yīng)該就是定義了對(duì)外擴(kuò)展的接口。這個(gè)函數(shù)的原型是:

PyAPI_FUNC(int) PyArg_ParseTuple(PyObject *, const char *, ...)

該函數(shù)的功能是將Python對(duì)象C/C++類型數(shù)據(jù),如果轉(zhuǎn)換失敗,返回0

第一個(gè)參數(shù):包含從Python傳遞到C函數(shù)的參數(shù)列表的元組對(duì)象

第二個(gè)參數(shù):是格式參數(shù),必須是字符串,已經(jīng)預(yù)定義好了的,零個(gè)或多個(gè)“格式單位”組成。一個(gè)格式單元描述一個(gè)Python對(duì)象。比如例子中的‘i'表示將Python整數(shù)對(duì)象轉(zhuǎn)換為純C語(yǔ)言的 int類型。

其余參數(shù):其余參數(shù)必須是其類型由格式字符串確定的變量的地址,可以是多個(gè)地址。上面例子用的就      是num的地址&num表示的就是num的地址,&是取值運(yùn)算符

一些常見(jiàn)的格式參數(shù):

"s":將Python字符串或Unicode對(duì)象轉(zhuǎn)換為C里面字符串的指針,即 Python中string o或者Unicode 對(duì)象轉(zhuǎn)換為C語(yǔ)言里 char *

“s?!保骸皊”上的這個(gè)變體存儲(chǔ)到兩個(gè)C變量中,第一個(gè)是指向字符串的指針,第二個(gè)是它的長(zhǎng)度。在這種情況下,Python字符串可能包含嵌入的空字節(jié)。如果可以進(jìn)行這種轉(zhuǎn)換,Unicode對(duì)象將傳回指向?qū)ο蟮哪J(rèn)編碼字符串版本的指針。所有其他讀緩沖區(qū)兼容對(duì)象傳回對(duì)原始內(nèi)部數(shù)據(jù)表示的引用。即(字符串,Unicode或任何讀取緩沖區(qū)兼容對(duì)象)→[char *,int]。

“z”:像“s”,但Python對(duì)象也可以是None,在這種情況下,C指針設(shè)置為NULL。即string或None)→[char *]

“z#”:(字符串或無(wú)或任何讀緩沖區(qū)兼容對(duì)象)→[char *,int]。

“u”:將Python Unicode對(duì)象轉(zhuǎn)換為C指針,指向16位Unicode(UTF-16)數(shù)據(jù)的空終止緩沖區(qū)。即(Unicode對(duì)象)→[Py_UNICODE *] 。

“u#”:這個(gè)變量“u”存儲(chǔ)到兩個(gè)C變量中,第一個(gè)是指向Unicode數(shù)據(jù)緩沖區(qū)的指針,第二個(gè)是它的長(zhǎng)度。(Unicode對(duì)象)→[Py_UNICODE *,int]。

“es”:“s”上的此變體用于將Unicode和可轉(zhuǎn)換為Unicode的對(duì)象編碼為字符緩沖區(qū)。它只適用于沒(méi)有嵌入NULL字節(jié)的編碼數(shù)據(jù)。變量讀取一個(gè)變量并存儲(chǔ)到兩個(gè)C變量中,第一個(gè)是指向編碼名稱字符串(編碼)的指針,第二個(gè)是指向字符緩沖區(qū)的指針的指針,即(字符串,Unicode對(duì)象或字符緩沖區(qū)兼容對(duì)象)→[const char * encoding,char ** buffer]。

“es?!保侯愃啤眅s",只是第三個(gè)指向整數(shù)的指針(* buffer_length,緩沖區(qū)長(zhǎng)度)。編碼名稱必須映射到注冊(cè)的編×××。如果設(shè)置為NULL,則使用默認(rèn)編碼。即(字符串,Unicode對(duì)象或字符緩沖區(qū)兼容對(duì)象)→[const char * encoding,char ** buffer,int * buffer_length]。

“h”:將Python整數(shù)轉(zhuǎn)換為C short int,即(integer)→[short int]

“i”:將Python整數(shù)轉(zhuǎn)換為純C int。即(integer)→[int]

“l(fā)”:將Python整數(shù)轉(zhuǎn)換為C long int,即(integer)→[long int]

“c”:將一個(gè)Python字符(表示為長(zhǎng)度為1的字符串)轉(zhuǎn)換為C char,即(長(zhǎng)度為1的字符串)→[char]

“f”:將Python浮點(diǎn)數(shù)轉(zhuǎn)換為C浮點(diǎn),即(float)→[float]

“d”:將Python浮點(diǎn)數(shù)轉(zhuǎn)換為C double,即(float)→[double]

“D”:將Python復(fù)雜數(shù)字轉(zhuǎn)換為C Py_complex結(jié)構(gòu),即(復(fù)合物)→[Py_complex]

“O”:將Python對(duì)象(無(wú)任何轉(zhuǎn)換)存儲(chǔ)在C對(duì)象指針中。 C程序因此接收被傳遞的實(shí)際對(duì)象。對(duì)象的引用計(jì)數(shù)不增加。存儲(chǔ)的指針不為NULL。(object)→[PyObject *]

“O!":將Python對(duì)象存儲(chǔ)在C對(duì)象指針。這類似于“O”,但有兩個(gè)C參數(shù):第一個(gè)是Python類型對(duì)象的地址,第二個(gè)是存儲(chǔ)對(duì)象指針的C變量(類型PyObject *)的地址。如果Python對(duì)象沒(méi)有必需的類型,則會(huì)引發(fā)TypeError。(object)→[typeobject,PyObject *]

“O&”:通過(guò)轉(zhuǎn)換器函數(shù)將Python對(duì)象轉(zhuǎn)換為C變量。這需要兩個(gè)參數(shù):第一個(gè)是一個(gè)函數(shù),第二個(gè)是C變量(任意類型)的地址,(object)→[converter,anything]

“S”:像“O”,但要求Python對(duì)象是一個(gè)字符串對(duì)象。如果對(duì)象不是字符串對(duì)象,則引發(fā)TypeError。 C變量也可以聲明為PyObject *。(string)→[PyStringObject *]

“u”:像“O”,但要求Python對(duì)象是一個(gè)Unicode對(duì)象。如果對(duì)象不是Unicode對(duì)象,則引發(fā)TypeError。 C變量也可以聲明為PyObject *。(Unicode字符串)→[PyUnicodeObject *]

“t#”:類似“s?!?,但接受任何實(shí)現(xiàn)只讀緩沖區(qū)接口的對(duì)象。 char *變量被設(shè)置為指向緩沖區(qū)的第一個(gè)字節(jié),int被設(shè)置為緩沖區(qū)的長(zhǎng)度。只接受單段緩沖對(duì)象;對(duì)所有其他類型引發(fā)TypeError。(只讀字符緩沖區(qū))→[char *,int]

“w”:類似于“s”,但接受實(shí)現(xiàn)讀寫(xiě)緩沖器接口的任何對(duì)象。調(diào)用者必須通過(guò)其他方式確定緩沖區(qū)的長(zhǎng)度,或者使用“w?!?。只接受單段緩沖對(duì)象;對(duì)所有其他類型引發(fā)TypeError。(讀寫(xiě)字符緩沖區(qū))→[char *]

“w?!保侯愃啤皊?!保邮苋魏螌?shí)現(xiàn)讀寫(xiě)緩沖區(qū)接口的對(duì)象。 char *變量被設(shè)置為指向緩沖區(qū)的第一個(gè)字節(jié),int被設(shè)置為緩沖區(qū)的長(zhǎng)度。只接受單段緩沖對(duì)象;對(duì)所有其他類型引發(fā)TypeError。(讀寫(xiě)字符緩沖區(qū))→[char *,int]

“items”:對(duì)象必須是Python序列,其長(zhǎng)度是項(xiàng)目中的格式單位數(shù)。 C參數(shù)必須對(duì)應(yīng)于各個(gè)格式單元initem。 可以嵌套序列的格式單元。(tuple)→[matching-items]


如果對(duì)Python源碼稍微有點(diǎn)了解的話,PyObject 、PyStringObject 、PyUnicodeObject等都是Python源碼里用C語(yǔ)言為Python定義的類型,有興趣的可以看看《Python源碼解析》這本書(shū),里面都有介紹。


另外還有一些其他字符在格式字符串中有意義,

“|”:表示Python參數(shù)列表中的其余參數(shù)是可選的。 對(duì)應(yīng)于可選參數(shù)的C變量應(yīng)該被初始化為它們的默認(rèn)值 - 當(dāng)沒(méi)有指定可選參數(shù)時(shí),PyArg_ParseTuple()不觸及相應(yīng)的C變量的內(nèi)容。

“:”:格式單元列表在這里結(jié)束; 冒號(hào)之后的字符串用作錯(cuò)誤消息中的函數(shù)(“PyArg_ParseTuple()”引發(fā)的異常的“關(guān)聯(lián)值”)。

“;”:格式單元列表在這里結(jié)束; 冒號(hào)之后的字符串用作錯(cuò)誤消息,而不是默認(rèn)錯(cuò)誤消息。 顯然,“:”和“;” 互相排斥。


(2)Py_BuildValue()

     該函數(shù)也是定義在/usr/include/python2.7/modsupport.h里,原型如下:

PyAPI_FUNC(PyObject *) Py_BuildValue(const char *, ...)


它的功能與PyArg_ParseTuple()正好相反,它是將C類型的數(shù)據(jù)結(jié)構(gòu)轉(zhuǎn)換成Python對(duì)象。

它第一個(gè)參數(shù)是格式參數(shù),同PyArg_ParseTuple()格式參數(shù)一樣,其余參數(shù)就是一些C類型的數(shù)據(jù)咯。

看幾個(gè)例子吧,對(duì)它的理解會(huì)有幫助。

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'  #這個(gè)還有一個(gè)長(zhǎng)度限制

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()

該函數(shù)也是定義在/usr/include/python2.7/modsupport.h里,返回一個(gè)指針指向剛創(chuàng)建的模塊對(duì)象,看名字也知道是初始化新建模塊的。函數(shù)原型:

#define Py_InitModule(name, methods) \
	Py_InitModule4(name, methods, (char *)NULL, (PyObject *)NULL, \
		       PYTHON_API_VERSION)

是宏定義,接受兩個(gè)參數(shù),第一個(gè)參數(shù)為字符串,表示模塊的名稱;第二個(gè)參數(shù)是一個(gè)PyMethodDef的結(jié)構(gòu)體數(shù)組,表示該模塊都具有哪些方法。

PyMethodDef結(jié)構(gòu)體有四個(gè)字段。 

* 第一個(gè)是一個(gè)字符串,表示在Python中對(duì)應(yīng)的方法的名稱; 

* 第二個(gè)是對(duì)應(yīng)的C代碼的函數(shù); 

* 第三個(gè)是一個(gè)標(biāo)致位,表示該P(yáng)ython方法是否需要參數(shù),METH_NOARGS表示不需要參數(shù),METH_VARARGS表示需要參數(shù),這個(gè)參數(shù)在/usr/include/python2.7/methodobject.h有定義; 

* 第四個(gè)是一個(gè)字符串,它是該方法的__doc__屬性,這個(gè)不是必須的,可以為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結(jié)構(gòu)體數(shù)組最后以 {NULL, NULL, 0, NULL}結(jié)尾。(感覺(jué)好像不是必須的,但是通常都這么做那我們也這么做吧)不正之處,歡迎批評(píng)指正!


向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI