日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > python >内容正文

python

Python与C之间的相互调用(Python C API及Python ctypes库)

發布時間:2023/12/15 python 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Python与C之间的相互调用(Python C API及Python ctypes库) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

14237人閱讀 評論(11) 收藏 舉報

目錄(?)[-]

  • Python C API
  • 準備工作:
  • C中內嵌Python
  • 獲取返回值
  • 利用C擴展Python
  • Python ctypes
  • write by 九天雁翎(JTianLing) -- blog.csdn.net/vagrxie

    討論新聞組及文件

    我實現onekeycodehighlighter " 中碰到的一些小問題,需要實現全局快捷鍵,但是是事實上Qt并沒有對全局快捷鍵提供支持,那么用Qt的話就只能通過Win32Api來完成了,而我,用的是PyQt,還需要用Python來調用win32 API,事實上,都沒有什么難的。

    因為Python如此的流行,導致,開源社區按照自己的愛好,對于Python與C之間互相調用上,各自開發了自己想要的調用方式,其中包括用 Python C API來完成,包括ctypes這個Python標準庫,還有那一大堆的各式各樣的綁定方案如SIP,Boost::Python等,要知 道,Python流行到什么程序,Boost庫號稱C++準標準庫,唯一對C++以外的一種語言提供了支持,那就是Python,Python還是 Symbian除C++,JAVA外支持的第3種語言,當年在原來的公司,我還一直以為Python是個新鮮的小玩意兒,要我鼓搗Python C API的時候很新鮮,(事實上原公司的確沒有用Python的人)到了新公司一看,啊~~~公司只允許使用3中語言,C++,JAVA,還有 Python,而大家對Python那都是駕輕就熟,信手拈來,常用來開發一些工具及腳本,呵呵,世界原來與我想象的并不同。
    這里將以前工作中用到的Python C API知識,及最近用到的ctypes庫的知識梳理一下。

    Python C API

    此部分可以參考我原來的文章《python c api 使用心得... 》,這里只是會有一些實際的例子,原來那是一個大概流程的描述。
    某年某月,在我開始學習Python古老的歲月中(我不是倚老賣老啊)。。。。ctypes還不存在,那時候我們都是老實的用C語言,調用Python C API來完成從Python中調用C語言函數的任務,我學習Python的時候還在想,哈哈哈哈哈,我以前學過C/C++,我可以很熟練的調用 Python C API來完成Python調用Win32 API這樣的任務,我多了不起啊:)這個時候的感覺就像,嘿,Python你不是了不起嗎。。。。還不是沒有辦法逃離C語言的魔掌。。。。此時,畫面中出 現的是K&R嘿嘿嘿嘿的冷笑。。。。Guido van Rossum在他們腳下抱著頭哭了。。。。。。。
    那時候,情況大概是這樣的:

    準備工作:

    閑話少說,看看Python C API。事實上,Python C API比起Lua的API了來說,清晰了很多,這也符合Pythonic的風格,就算這時Python C API是設計給C語言使用者使用的,還是這樣的風格,比起Lua API那種匯編式的接口,(據說為了效率,可以直接操作每個數據)強了太多了。
    要使用Python C API,用普通的二進制包是不行的,得下源碼包。這里我用3.1.1的源碼包為例:Source Distribution
    Python的源碼在Windows的版本中已經完全換到VS2008了,直接用VS2008打開在PCbuild目錄下的工程即可,對于VS2005及 以前的用戶打開PC目錄下的其他版本工程。我們編譯debug版本的pythoncore會得到 python31_d.lib,python31_d.dll兩個文件,需要的頭文件在Include目錄下,還需要將pyconfig.h文件從 PCBuild目錄下拷貝到Include中,(硬要直接指定也可以)這樣準備工作就已經齊了。

    Python C API有兩個方向的使用方式,從C中調用Python腳本及利用C擴展Python。
    先講簡單的從C中調用Python,也就是常說的在C中內嵌Python。

    C中內嵌Python

    新建立一個工程,首先需要將工作目錄設置到Python-3.1.1PCbuild中,以獲取到動態庫,至于靜態庫的包含,Include目錄的指定,那自然也是少不了的。文件中需要包含Python.h文件,這也是必須的。
    接口中
    ??? Py_Initialize();
    ?? ?Py_Finalize();
    一對的調用是必須的,一個用于初始化Python的動態庫,一個用于釋放。釋放時會輸出[31818 refs],意義不明。

    PyRun_SimpleString

    可用于執行簡單的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]
    請按任意鍵繼續. . .

    ?

    此時可以執行一些Python語句了,并且,特別需要注意的是,在一個Py_Initialize();與Py_Finalize();之間,Python語句執行是在同一個執行環境中,不懂什么意思?看個示例就知道了。


    int ?main(int ?argc, char * argv[])
    {
    ????Py_Initialize();

    ????PyRun_SimpleString("str = " Hello World " " );
    ????PyRun_SimpleString("print(str)" );

    ????Py_Finalize();

    ????system("PAUSE" );
    ????return ?0 ;
    }

    此例與上例輸出是一樣的,懂我的意思了吧?意思就是以前執行的語句對后面的語句是有效的,相當于在同一個交互式命令行中順序執行語句。

    獲取返回值

    PyRun_SimpleString有的缺點,文檔中的描述是:

    Returns 0 on success or -1 if an exception was raised.

    那么你就無法在Python及C語言中傳遞任何信息。我們需要高級點的函數才行。

    ?

    PyObject* PyRun_String(const char *str, int start, PyObject *globals, PyObject *locals)
    就是干這個的。
    但是需要注意的是此函數的一些參數的獲取,按照想當然的給他們置空可是不行的,如下例所示:

    #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__ 模塊中運行,注意啊,沒有導入正確的模塊及其dict,你會運行失敗,失敗的很慘。至此,C語言已經于Python來了個交互了。
    呵呵,突然覺得深入下去就沒有盡頭了。。。。。。。還是點到為止吧。
    稍微深入點的可以去看《Programming Python》一書。在啄木鳥 上有此書及一些譯文。Part VI: Integration 部分Chapter 23. Embedding Python,有相關的知識。

    利用C擴展Python

    此部分在《Programming Python》的Chapter 22. Extending Python 部分有介紹。
    這里也只能開個頭了,最多告訴你,其實,這些都沒有什么難的。稍微復雜點的情況《python c api 使用心得... 》一文中有介紹。
    配置上與前面講的類似,一般來說,利用C擴展Python最后會生成一個動態庫,不過這個動態庫的后綴會設為.pyd,只有這樣,import的時候才會自動的查詢到。
    另外,為Python寫擴展要遵循Python的那套規則,固定的幾個命名。
    首先看自帶的例子:

    #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寫擴展時的基本信息:
    1.PyInit_example是最后的出口,其中需要注意的是example不僅僅代表example的意思,還代表了最后生成的庫會用example命名,也就是你調用此庫會需要使用
    import example

    的形式。
    2.static ?struct ?PyModuleDef examplemodule的存在也是必須的,指定了整個模塊的信息,比如上面 的"example module doc string", 模塊的說明文字。每個參數的含義上面已經有些演示了。 全部內容可以參考文檔中關于PyModuleDef的說明
    3.example_methods是一個函數列表,事實上表示此模塊中含有的函數。此例中僅含有 foo一個函數。
    static ?PyObject *
    ex_foo(PyObject *self, PyObject *args)
    {
    ????printf("Hello, world n " );
    ????Py_INCREF(Py_None);
    ????return ?Py_None;
    }

    就是整個函數的具體實現了,此函數表示輸出"Hello, world",還是hello world。。。。。。。。這個world還真忙啊。。。。天天有人say hello。

    這個Python本身附帶的例子有點太簡單了,我給出一個稍微復雜點的例子,還是我最喜歡的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);
    }

    需要注意的還是需要注意,唯一有點區別的是這里我有從Python中傳進來的參數及從C中傳出去的返回值了。
    PyArg_ParseTuple 用于解析參數
    Py_BuildValue 用于構建一個Python的值返回
    他們的構建和解析形式有點類似于sprintf等C常見的形式,可是每個字符代表的東西不一定一樣,需要注意,文檔中比較詳細,此例中展示的是String及int的轉換。

    以生成動態庫的方式編譯此文件后,并指定為Win32API.pyd文件,然后將其拷貝到Python_d所在的目錄(用Python3.1.1源代碼生成的調試版本Python),此時import會首先查找*_d.pyd形式的動態庫,不然只會搜索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的強大。。。。。。。。。。。。。此時MessageBox已經在Win32API中了,直接調用吧。我這里忽略了窗口的句柄,需要注意。

    多么繁忙的World啊。。。。。。。。
    此時你會想,太強大了,我要將整個的Win32 API到處,于是Python就能像C/C++語言一樣完全操作整個操作系統了,并且,這還是動態的!!!!
    沒錯,不過多大的工作量啊。。。。。。不過,Python這么流行,總是有人做這樣的事情的,于是PyWindows出世了。去安裝一個,于是你什么都有了。
    >>> import win32api
    >>> win32api.MessageBox(0, "Great", "Hello World", 0)
    1
    這樣,就能達到上面全部的效果。。。。。。。。。。。

    ?

    Python ctypes

    如此這般,原來Python還是離不開C啊(雖然Python本身使用C寫的)。。。,直到。。。。某年某月ctypes橫空出世了,于是,完全不 懂C語言的人,也可以直接用Python來完成這樣的工作了。毫無疑問,Python越來越自成體系了,他們的目標是,沒有其他語言!-_-!在 Python v3.1.1的文檔中如此描述,
    ctypes — A foreign function library for Python
    然后:It can be used to wrap these libraries in pure Python.
    注意,他們要的是Pure Python!(我不是想要挑起語言戰爭。。。。。)
    Guido van Rossum開始說,wrap these,in pure Python。。。。不要再用foreign語言,血統不pure的家伙了。


    閑話少說,看看ctypes,因為是pure Python嘛,所以看起來很簡單,事實上文檔也比較詳細(當然,還是遺漏了一些細節),下面都以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語言的類型定義。
    當你import ctypes的時候,一些動態庫已經載入了:
    >>> print(windll.kernel32)

    >>> print(windll.user32)

    >>> print(windll.msvcrt)


    直接來使用試試吧,我們最喜歡的自然是Hello World。這里直接調用MessageBox。查查MSDN,MessageBox在User32中,我們調用它。
    >>> MessageBox = windll.user32.MessageBoxW
    >>> MessageBox(0,"Great","Hello World", 0)
    然后,就調用了MessageBox了。。。。。。。。

    怎么?暈了?比較一下ctypes庫及Python C API吧。。。。于是,K&R哭了。。。。。。。。。。。。。
    故事以下圖開始

    以下圖結束:




    轉載于:https://www.cnblogs.com/langqi250/archive/2012/11/02/2750659.html

    總結

    以上是生活随笔為你收集整理的Python与C之间的相互调用(Python C API及Python ctypes库)的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。