此部分可以參考我原來的文章《python c api 使用心得... 》,這里只是會有一些實際的例子,原來那是一個大概流程的描述。
某年某月,在我開始學(xué)習(xí)Python古老的歲月中(我不是倚老賣老啊)。。。。ctypes還不存在,那時候我們都是老實的用C語言,調(diào)用Python C API來完成從Python中調(diào)用C語言函數(shù)的任務(wù),我學(xué)習(xí)Python的時候還在想,哈哈哈哈哈,我以前學(xué)過C/C++,我可以很熟練的調(diào)用 Python C API來完成Python調(diào)用Win32 API這樣的任務(wù),我多了不起?。海┻@個時候的感覺就像,嘿,Python你不是了不起嗎。。。。還不是沒有辦法逃離C語言的魔掌。。。。此時,畫面中出現(xiàn)的是K&R嘿嘿嘿嘿的冷笑。。。。Guido van Rossum在他們腳下抱著頭哭了。。。。。。。
那時候,情況大概是這樣的:
創(chuàng)新互聯(lián)專注于獨山子企業(yè)網(wǎng)站建設(shè),成都響應(yīng)式網(wǎng)站建設(shè)公司,電子商務(wù)商城網(wǎng)站建設(shè)。獨山子網(wǎng)站建設(shè)公司,為獨山子等地區(qū)提供建站服務(wù)。全流程按需設(shè)計網(wǎng)站,專業(yè)設(shè)計,全程項目跟蹤,創(chuàng)新互聯(lián)專業(yè)和態(tài)度為您提供的服務(wù)
閑話少說,看看Python C API。事實上,Python C API比起Lua的API了來說,清晰了很多,這也符合Pythonic的風(fēng)格,就算這時Python C API是設(shè)計給C語言使用者使用的,還是這樣的風(fēng)格,比起Lua API那種匯編式的接口,(據(jù)說為了效率,可以直接操作每個數(shù)據(jù))強(qiáng)了太多了。
要使用Python C API,用普通的二進(jìn)制包是不行的,得下源碼包。這里我用3.1.1的源碼包為例:Source Distribution
Python的源碼在Windows的版本中已經(jīng)完全換到VS2008了,直接用VS2008打開在PCbuild目錄下的工程即可,對于VS2005及以前的用戶打開PC目錄下的其他版本工程。我們編譯debug版本的pythoncore會得到python31_d.lib,python31_d.dll兩個文件,需要的頭文件在Include目錄下,還需要將pyconfig.h文件從PCBuild目錄下拷貝到Include中,(硬要直接指定也可以)這樣準(zhǔn)備工作就已經(jīng)齊了。
Python C API有兩個方向的使用方式,從C中調(diào)用Python腳本及利用C擴(kuò)展Python。
先講簡單的從C中調(diào)用Python,也就是常說的在C中內(nèi)嵌Python。
新建立一個工程,首先需要將工作目錄設(shè)置到Python-3.1.1PCbuild中,以獲取到動態(tài)庫,至于靜態(tài)庫的包含,Include目錄的指定,那自然也是少不了的。文件中需要包含Python.h文件,這也是必須的。
接口中
Py_Initialize();
Py_Finalize();
一對的調(diào)用是必須的,一個用于初始化Python的動態(tài)庫,一個用于釋放。釋放時會輸出[31818 refs],意義不明。
PyRun_SimpleString
可用于執(zhí)行簡單的Python語句。如下:
#include "python.h"
int main(int argc, char * argv[])
{
Py_Initialize();
PyRun_SimpleString("print( " Hello World " )" );
Py_Finalize();
system("PAUSE" );
return 0 ;
}
此時,輸出為:
Hello World
[31829 refs]
請按任意鍵繼續(xù). . .
此時可以執(zhí)行一些Python語句了,并且,特別需要注意的是,在一個Py_Initialize();與Py_Finalize();之間,Python語句執(zhí)行是在同一個執(zhí)行環(huán)境中,不懂什么意思?看個示例就知道了。
int main(int argc, char * argv[])
{
Py_Initialize();
PyRun_SimpleString("str = " Hello World " " );
PyRun_SimpleString("print(str)" );
Py_Finalize();
system("PAUSE" );
return 0 ;
}
此例與上例輸出是一樣的,懂我的意思了吧?意思就是以前執(zhí)行的語句對后面的語句是有效的,相當(dāng)于在同一個交互式命令行中順序執(zhí)行語句。
PyRun_SimpleString有的缺點,文檔中的描述是:
Returns 0 on success or -1 if an exception was raised.
那么你就無法在Python及C語言中傳遞任何信息。我們需要高級點的函數(shù)才行。
PyObject* PyRun_String(const char *str, int start, PyObject *globals, PyObject *locals)
就是干這個的。
但是需要注意的是此函數(shù)的一些參數(shù)的獲取,按照想當(dāng)然的給他們置空可是不行的,如下例所示:
#include "python.h"
int main(int argc, char * argv[])
{
Py_Initialize();
PyRun_SimpleString("x = 10" );
PyRun_SimpleString("y = 20" );
PyObject* mainModule = PyImport_ImportModule("__main__" );
PyObject* dict = PyModule_GetDict(mainModule);
PyObject* resultObject = PyRun_String("x + y" , Py_eval_input, dict, dict);
if (resultObject)
{
long result = PyLong_AsLong(resultObject);
printf(" %d " , result);
Py_DECREF(resultObject);
}
Py_Finalize();
system("PAUSE" );
return 0 ;
}
這里我利用了一個知識,那就是 PyRun_SimpleString實際是將所有的代碼都放在 __main__ 模塊中運行,注意啊,沒有導(dǎo)入正確的模塊及其dict,你會運行失敗,失敗的很慘。至此,C語言已經(jīng)于Python來了個交互了。
呵呵,突然覺得深入下去就沒有盡頭了。。。。。。。還是點到為止吧。
稍微深入點的可以去看《Programming Python》一書。在啄木鳥 上有此書及一些譯文。Part VI: Integration 部分Chapter 23. Embedding Python,有相關(guān)的知識。
此部分在《Programming Python》的Chapter 22. Extending Python 部分有介紹。
這里也只能開個頭了,最多告訴你,其實,這些都沒有什么難的。稍微復(fù)雜點的情況《python c api 使用心得... 》一文中有介紹。
配置上與前面講的類似,一般來說,利用C擴(kuò)展Python最后會生成一個動態(tài)庫,不過這個動態(tài)庫的后綴會設(shè)為.pyd,只有這樣,import的時候才會自動的查詢到。
另外,為Python寫擴(kuò)展要遵循Python的那套規(guī)則,固定的幾個命名。
首先看自帶的例子:
#include "Python.h"
static PyObject *
ex_foo(PyObject *self, PyObject *args)
{
printf("Hello, world n " );
Py_INCREF(Py_None);
return Py_None;
}
static PyMethodDef example_methods[] = {
{"foo" , ex_foo, METH_VARARGS, "foo() doc string" },
{NULL , NULL }
};
static struct PyModuleDef examplemodule = {
PyModuleDef_HEAD_INIT,
"example" ,
"example module doc string" ,
-1 ,
example_methods,
NULL ,
NULL ,
NULL ,
NULL
};
PyMODINIT_FUNC
PyInit_example(void )
{
return PyModule_Create(&examplemodule);
}
這個例子包含了全部C語言為Python寫擴(kuò)展時的基本信息:
1.PyInit_example是最后的出口,其中需要注意的是example不僅僅代表example的意思,還代表了最后生成的庫會用example命名,也就是你調(diào)用此庫會需要使用
import example
的形式。
2.static struct PyModuleDef examplemodule的存在也是必須的,指定了整個模塊的信息,比如上面 的"example module doc string", 模塊的說明文字。每個參數(shù)的含義上面已經(jīng)有些演示了。 全部內(nèi)容可以參考文檔中關(guān)于PyModuleDef的說明
3.example_methods是一個函數(shù)列表,事實上表示此模塊中含有的函數(shù)。此例中僅含有 foo一個函數(shù)。
static PyObject *
ex_foo(PyObject *self, PyObject *args)
{
printf("Hello, world n " );
Py_INCREF(Py_None);
return Py_None;
}
就是整個函數(shù)的具體實現(xiàn)了,此函數(shù)表示輸出"Hello, world",還是hello world。。。。。。。。這個world還真忙啊。。。。天天有人say hello。
這個Python本身附帶的例子有點太簡單了,我給出一個稍微復(fù)雜點的例子,還是我最喜歡的MessageBox,最后的效果自然還是Hello world。。。。。。。。。。。
#include
static PyObject *
MessageBox(PyObject *self, PyObject *args)
{
LPCSTR lpText;
LPCSTR lpCaption;
UINT uType;
PyArg_ParseTuple(args, "ssi" , &lpText, &lpCaption, &uType);
int result = MessageBoxA(0 , lpText, lpCaption, uType);
PyObject* resultObject = Py_BuildValue(" %i " , result);
return resultObject;
}
static PyMethodDef c_methods[] = {
{"MessageBox" , MessageBox, METH_VARARGS, "MessageBox() " },
{NULL , NULL }
};
static struct PyModuleDef win32module = {
PyModuleDef_HEAD_INIT,
"Win32API" ,
"Win32 API MessageBox" ,
-1 ,
c_methods,
NULL ,
NULL ,
NULL ,
NULL
};
PyMODINIT_FUNC
PyInit_Win32API(void )
{
return PyModule_Create(&win32module);
}
需要注意的還是需要注意,唯一有點區(qū)別的是這里我有從Python中傳進(jìn)來的參數(shù)及從C中傳出去的返回值了。
PyArg_ParseTuple 用于解析參數(shù)
Py_BuildValue 用于構(gòu)建一個Python的值返回
他們的構(gòu)建和解析形式有點類似于sprintf等C常見的形式,可是每個字符代表的東西不一定一樣,需要注意,文檔中比較詳細(xì),此例中展示的是String及int的轉(zhuǎn)換。
以生成動態(tài)庫的方式編譯此文件后,并指定為Win32API.pyd文件,然后將其拷貝到Python_d所在的目錄(用Python3.1.1源代碼生成的調(diào)試版本Python),此時import會首先查找*_d.pyd形式的動態(tài)庫,不然只會搜索release版。
首先看看庫的信息:
>>> import Win32API
[44692 refs]
>>> dir(Win32API)
['MessageBox', '__doc__', '__file__', '__name__', '__package__']
[44705 refs]
>>> help(Win32API)
Help on module Win32API:
NAME
Win32API - Win32 API MessageBox
FILE
d:python-3.1.1pcbuildwin32api_d.pyd
FUNCTIONS
MessageBox(...)
MessageBox()
[68311 refs]
注意到文檔的作用了吧?還注意到dir的強(qiáng)大。。。。。。。。。。。。。此時MessageBox已經(jīng)在Win32API中了,直接調(diào)用吧。我這里忽略了窗口的句柄,需要注意。
多么繁忙的World啊。。。。。。。。
此時你會想,太強(qiáng)大了,我要將整個的Win32 API到處,于是Python就能像C/C++語言一樣完全操作整個操作系統(tǒng)了,并且,這還是動態(tài)的?。。?!
沒錯,不過多大的工作量啊。。。。。。不過,Python這么流行,總是有人做這樣的事情的,于是PyWindows出世了。去安裝一個,于是你什么都有了。
>>> import win32api
>>> win32api.MessageBox(0, "Great", "Hello World", 0)
1
這樣,就能達(dá)到上面全部的效果。。。。。。。。。。。
如此這般,原來Python還是離不開C?。m然Python本身使用C寫的)。。。,直到。。。。某年某月ctypes橫空出世了,于是,完全不懂C語言的人,也可以直接用Python來完成這樣的工作了。毫無疑問,Python越來越自成體系了,他們的目標(biāo)是,沒有其他語言!-_-!在Python v3.1.1的文檔中如此描述,
ctypes — A foreign function library for Python
然后:It can be used to wrap these libraries in pure Python.
注意,他們要的是Pure Python?。ㄎ也皇窍胍羝鹫Z言戰(zhàn)爭。。。。。)
Guido van Rossum開始說,wrap these,in pure Python。。。。不要再用foreign語言,血統(tǒng)不pure的家伙了。
閑話少說,看看ctypes,因為是pure Python嘛,所以看起來很簡單,事實上文檔也比較詳細(xì)(當(dāng)然,還是遺漏了一些細(xì)節(jié)),下面都以Windows中的Python3.1.1的操作為例:
>>> import ctypes
>>> from ctypes import *
>>> dir(ctypes)
['ARRAY', 'ArgumentError', 'Array', 'BigEndianStructure', 'CDLL', 'CFUNCTYPE', '
DEFAULT_MODE', 'DllCanUnloadNow', 'DllGetClassObject', 'FormatError', 'GetLastEr
ror', 'HRESULT', 'LibraryLoader', 'LittleEndianStructure', 'OleDLL', 'POINTER',
'PYFUNCTYPE', 'PyDLL', 'RTLD_GLOBAL', 'RTLD_LOCAL', 'SetPointerType', 'Structure
', 'Union', 'WINFUNCTYPE', 'WinDLL', 'WinError', '_CFuncPtr', '_FUNCFLAG_CDECL',
'_FUNCFLAG_PYTHONAPI', '_FUNCFLAG_STDCALL', '_FUNCFLAG_USE_ERRNO', '_FUNCFLAG_U
SE_LASTERROR', '_Pointer', '_SimpleCData', '__builtins__', '__doc__', '__file__'
, '__name__', '__package__', '__path__', '__version__', '_c_functype_cache', '_c
alcsize', '_cast', '_cast_addr', '_check_HRESULT', '_check_size', '_ctypes_versi
on', '_dlopen', '_endian', '_memmove_addr', '_memset_addr', '_os', '_pointer_typ
e_cache', '_string_at', '_string_at_addr', '_sys', '_win_functype_cache', '_wstr
ing_at', '_wstring_at_addr', 'addressof', 'alignment', 'byref', 'c_bool', 'c_buf
fer', 'c_byte', 'c_char', 'c_char_p', 'c_double', 'c_float', 'c_int', 'c_int16',
'c_int32', 'c_int64', 'c_int8', 'c_long', 'c_longdouble', 'c_longlong', 'c_shor
t', 'c_size_t', 'c_ubyte', 'c_uint', 'c_uint16', 'c_uint32', 'c_uint64', 'c_uint
8', 'c_ulong', 'c_ulonglong', 'c_ushort', 'c_void_p', 'c_voidp', 'c_wchar', 'c_w
char_p', 'cast', 'cdll', 'create_string_buffer', 'create_unicode_buffer', 'get_e
rrno', 'get_last_error', 'memmove', 'memset', 'oledll', 'pointer', 'py_object',
'pydll', 'pythonapi', 'resize', 'set_conversion_mode', 'set_errno', 'set_last_er
ror', 'sizeof', 'string_at', 'windll', 'wstring_at']
一個這樣的小玩意兒包含的東西還真不少啊,可以看到主要包括一些C語言的類型定義。
當(dāng)你import ctypes的時候,一些動態(tài)庫已經(jīng)載入了:
>>> print(windll.kernel32)
>>> print(windll.user32)
>>> print(windll.msvcrt)
直接來使用試試吧,我們最喜歡的自然是Hello World。這里直接調(diào)用MessageBox。查查MSDN,MessageBox在User32中,我們調(diào)用它。
>>> MessageBox = windll.user32.MessageBoxW
>>> MessageBox(0,"Great","Hello World", 0)
然后,就調(diào)用了MessageBox了。。。。。。。。
分享標(biāo)題:Python與C之間的相互調(diào)用(PythonCAPI及Pythonctypes庫)
網(wǎng)站URL:http://chinadenli.net/article10/gpdedo.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供企業(yè)網(wǎng)站制作、靜態(tài)網(wǎng)站、面包屑導(dǎo)航、網(wǎng)站導(dǎo)航、網(wǎng)站內(nèi)鏈、網(wǎng)站營銷
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)