使用Python的ctypes,我們可以直接調(diào)用由C直接編譯出來的函數(shù)。其實就是調(diào)用動態(tài)鏈接庫中的函數(shù)。為什么我們需要這樣做呢,因為有些時候,我們可能需要一個性能上比較講究的算法,有些時候,我們可以在Python中使用已經(jīng)有了的現(xiàn)成的被封閉在動態(tài)鏈接庫中的函數(shù)。下面是如何調(diào)用的示例。
成都創(chuàng)新互聯(lián)服務項目包括港口網(wǎng)站建設(shè)、港口網(wǎng)站制作、港口網(wǎng)頁制作以及港口網(wǎng)絡(luò)營銷策劃等。多年來,我們專注于互聯(lián)網(wǎng)行業(yè),利用自身積累的技術(shù)優(yōu)勢、行業(yè)經(jīng)驗、深度合作伙伴關(guān)系等,向廣大中小型企業(yè)、政府機構(gòu)等提供互聯(lián)網(wǎng)行業(yè)的解決方案,港口網(wǎng)站推廣取得了明顯的社會效益與經(jīng)濟效益。目前,我們服務的客戶以成都為中心已經(jīng)輻射到港口省份的部分城市,未來相信會繼續(xù)擴大服務區(qū)域并繼續(xù)獲得客戶的支持與信任!
首先,我們用一個乘法來表示一個算法功能。下面是C的程序:
int?multiply(int?num1,?int?num2){???
return?num1?*?num2;
}????
如果在Windows下,你可能需要寫成下面這個樣子:
#include?windows.h?
BOOL?APIENTRYDll
Main(HANDLE?hModule,?DWORD?dwReason,?LPVOID?lpReserved){????
return?TRUE;
}?
__declspec(dllexport)?
intmultiply(int?num1,?int?num2){?
return?num1?*?num2;
}????
然后,自然是把這個C文件編成動態(tài)鏈接庫:
Linux下的編譯:
gcc?-c?-fPIC?libtest.c
gcc?-shared?libtest.o?-o?libtest.so????
Windows下的編譯:
cl?-LD?libtest.c?-libtest.dll????
于是在我們的Python中可以這樣使用:
(其中的libtest.so在Windows下改成libtest.dll即可)
from?ctypes?import?*
import?os
libtest?=?cdll.LoadLibrary(os.getcwd()?+?'/libtest.so')
print?libtest.multiply(2,?2)4????
注意:上面的Python腳本中需要把動態(tài)鏈接庫放到當前目錄中。
這個事情做過好多遍,摸索的過程基本這樣的:
1. 通過stdout通信...土到爆,但上手極快,簡單粗暴;
2. 調(diào)用原始的python.h 接口,編寫可以被python import 的so,支持python調(diào)用c++接口,c++接口調(diào)用python同樣的方式;
3. 使用boost-python 完成2中的功能,接口簡單很多,本質(zhì)上沒有不同;
這里遇到的主要幾個問題在于:
1. 數(shù)據(jù)的序列化反序列化,因為有時c++和python之間通信的不是基本類型,可能是用戶自定義類型;
2. 多線程的問題,c++多線程調(diào)python接口時,需要注意GIL的使用,貌似因為python解釋器不是線程安全的;
3.
對象傳遞,大多數(shù)情況下,如果只是靜態(tài)接口調(diào)用,都比較簡單,考慮一種情況:c++中的對象的一個函數(shù)調(diào)用python一個接口,這個python接口中
又需要反過來調(diào)用這個對象中的另一個接口,這里就需要考慮怎么把對象相互傳遞,我這里是把對象指針地址傳遞到python中,在python中調(diào)用一個
c++的靜態(tài)接口,帶上地址和其他需要的參數(shù),在這個c++的靜態(tài)接口中,把地址轉(zhuǎn)換成指針在調(diào)用..
要搞明白如何讓python調(diào)用C/C++代碼(也就是寫python的extension),你需要征服手冊中的Extending embedding厚厚的一章。在昨天花了一個小時看地頭暈腦脹,仍然不知道如何寫python的extension后,查閱了一些其他書籍,最終在Python Programming On Win32書中找到了教程。
1. 首先要明白的是,所謂的python擴展(也就是你提供給python的c/c++代碼,不一定是c/c++代碼,可以是其他語言寫的代碼)是一個dll,并且這個dll放在本機python安裝目錄下的DLLs目錄下(譬如我機器上的路徑是:F:/Program Files/Python25/DLLs),假如我們接下來要寫的擴展module名為mb,python調(diào)用的代碼為:import mbmb.showMsg("Python's really amazing, I kindda love it!")
2. 搭建環(huán)境,我們要使用python提供的c頭文件和lib庫來進行擴展的開發(fā)。
在vs 2005下點擊菜單 "工具"-"選項", 打開選項對話框,選擇"項目和解決方案-VC++目錄", 然后在右邊"顯示以下內(nèi)容的目錄"得comboBox上選擇"包含文件”,添加python的include目錄(我的機器上是"F:/Program Files/Python25/include"),然后選擇庫文件,添加python的libs目錄(我的機器上是"F:/Program Files/Python25/libs")。
既然擴展是一個dll,接下來我們要建立一個“動態(tài)鏈接庫”工程,然后開始寫代碼:
#include python.h //python.h是包含python一些定義的頭文件,在python的include目錄下/*我的python版本是2.5, 因為安裝python后它沒提供debug下的lib庫文件,因此你必須生成release版的dll,
想要生成dll版本的,你要到python官網(wǎng)上自己去下載python源代碼,當然你可以繼續(xù)生成release版本的dll,但dll中包含調(diào)試信息*/#pragma comment(lib, "python25.lib")//先不管static PyObject* mb_showMsg(PyObject* self, PyObject *args);/*如果你的擴展是mb,那么必須實現(xiàn)一個initmb函數(shù),并且從dll中導出這個函數(shù),但我們在python中調(diào)用import mb時,python會去dll里去調(diào)用
extern "C" __declspec(dllexport) void initmb(){/*當調(diào)用mb.showMsg("Python's really amazing, I kindda love it!")時, 相當于你告訴python我有一個showMsg函數(shù),我們怎么告訴python去調(diào)用我們dll里的mb_showMsg函數(shù)呢?技巧就是下面的方式,定義一個字典數(shù)據(jù)結(jié)構(gòu),key = showMsg, value =mb_showMsg,METH_VARARGS是函數(shù)調(diào)用方式,仔細查手冊吧*/static PyMethodDef mbMethods[] = {
{"showMsg", mb_showMsg, METH_VARARGS},
{NULL, NULL, NULL} /*sentinel,哨兵,用來標識結(jié)束*/};//告訴python我們的模塊名叫mb, 模塊包含的函數(shù)都在mbMethods字典里
PyObject *m = Py_InitModule("mb", mbMethods);}/*接下來實現(xiàn)核心功能showMsg*///第一個self參數(shù)我們用不著,具體查手冊,第二個參數(shù)是python傳給我們的參數(shù),它是一個python的參數(shù)tuple
static PyObject* mb_showMsg(PyObject* self, PyObject *args){//我們的showMsg函數(shù)需要的是一個字符串參數(shù)
const char* msg = NULL;/*調(diào)用特殊參數(shù)解碼python傳遞給我們的參數(shù),s是string,我們傳遞接收參數(shù)的變量地址,
如果你的功能函數(shù)需要兩個參數(shù),在PyArg_parseTuple后面繼續(xù)添加接受參數(shù)的變量地址,
這個函數(shù)的原型是類似printf的不定參數(shù)的形式
PyAPI_FUNC(int) PyArg_ParseTuple(PyObject *, const char *, ...);*/if (!PyArg_ParseTuple(args, "s", msg))
return NULL;//調(diào)用MBint r = ::MessageBox(NULL, "hello", "Caption:Form C module", MB_ICONINFORMATION | MB_OK);//返回值return Py_BuildValue("i", r);}將上面這段混雜著大量注釋的代碼拷貝到你的編輯器里,然后編譯生成mb.dll,修改后綴成mb.pyd,然后拷貝到python的DLLs目錄下,打開idle(python的交互程序),寫入代碼:import mbmb.showMsg("Python's really amazing, I kindda love it!")
Python是解釋性語言, 底層就是用c實現(xiàn)的, 所以用python調(diào)用C是很容易的, 下面就總結(jié)一下各種調(diào)用的方法, 給出例子, 所有例子都在ubuntu9.10, python2.6下試過
1. Python 調(diào)用 C (base)
想在python中調(diào)用c函數(shù), 如這兒的fact
#include Python.h
int fact(int n)
{
if (n = 1)
return 1;
else
return n * fact(n - 1);
}
PyObject* wrap_fact(PyObject* self, PyObject* args)
{
int n, result;
if (! PyArg_ParseTuple(args, "i:fact", n))
return NULL;
result = fact(n);
return Py_BuildValue("i", result);
}
static PyMethodDef exampleMethods[] =
{
{"fact", wrap_fact, METH_VARARGS, "Caculate N!"},
{NULL, NULL}
};
void initexample()
{
PyObject* m;
m = Py_InitModule("example", exampleMethods);
}
把這段代碼存為wrapper.c, 編成so庫,
gcc -fPIC wrapper.c -o example.so -shared -I/usr/include/python2.6 -I/usr/lib/python2.6/config
然后在有此so庫的目錄, 進入python, 可以如下使用
import example
example.fact(4)
2. Python 調(diào)用 C++ (base)
在python中調(diào)用C++類成員函數(shù), 如下調(diào)用TestFact類中的fact函數(shù),
#include Python.h
class TestFact{
public:
TestFact(){};
~TestFact(){};
int fact(int n);
};
int TestFact::fact(int n)
{
if (n = 1)
return 1;
else
return n * (n - 1);
}
int fact(int n)
{
TestFact t;
return t.fact(n);
}
PyObject* wrap_fact(PyObject* self, PyObject* args)
{
int n, result;
if (! PyArg_ParseTuple(args, "i:fact", n))
return NULL;
result = fact(n);
return Py_BuildValue("i", result);
}
static PyMethodDef exampleMethods[] =
{
{"fact", wrap_fact, METH_VARARGS, "Caculate N!"},
{NULL, NULL}
};
extern "C" //不加會導致找不到initexample
void initexample()
{
PyObject* m;
m = Py_InitModule("example", exampleMethods);
}
把這段代碼存為wrapper.cpp, 編成so庫,
g++ -fPIC wrapper.cpp -o example.so -shared -I/usr/include/python2.6 -I/usr/lib/python2.6/config
然后在有此so庫的目錄, 進入python, 可以如下使用
import example
example.fact(4)
3. Python 調(diào)用 C++ (Boost.Python)
Boost庫是非常強大的庫, 其中的python庫可以用來封裝c++被python調(diào)用, 功能比較強大, 不但可以封裝函數(shù)還能封裝類, 類成員.
首先在ubuntu下安裝boost.python, apt-get install libboost-python-dev
#include boost/python.hpp
char const* greet()
{
return "hello, world";
}
BOOST_PYTHON_MODULE(hello)
{
using namespace boost::python;
def("greet", greet);
}
把代碼存為hello.cpp, 編譯成so庫
g++ hello.cpp -o hello.so -shared -I/usr/include/python2.5 -I/usr/lib/python2.5/config -lboost_python-gcc42-mt-1_34_1
此處python路徑設(shè)為你的python路徑, 并且必須加-lboost_python-gcc42-mt-1_34_1, 這個庫名不一定是這個, 去/user/lib查
然后在有此so庫的目錄, 進入python, 可以如下使用
import hello
hello.greet()
'hello, world'
4. python 調(diào)用 c++ (ctypes)
ctypes is an advanced ffi (Foreign Function Interface) package for Python 2.3 and higher. In Python 2.5 it is already included.
ctypes allows to call functions in dlls/shared libraries and has extensive facilities to create, access and manipulate simple and complicated C data types in Python - in other words: wrap libraries in pure Python. It is even possible to implement C callback functions in pure Python.
#include Python.h
class TestFact{
public:
TestFact(){};
~TestFact(){};
int fact(int n);
};
int TestFact::fact(int n)
{
if (n = 1)
return 1;
else
return n * (n - 1);
}
extern "C"
int fact(int n)
{
TestFact t;
return t.fact(n);
}
將代碼存為wrapper.cpp不用寫python接口封裝, 直接編譯成so庫,
g++ -fPIC wrapper.cpp -o example.so -shared -I/usr/include/python2.6 -I/usr/lib/python2.6/config
進入python, 可以如下使用
import ctypes
pdll = ctypes.CDLL('/home/ubuntu/tmp/example.so')
pdll.fact(4)
12
在函數(shù)聲明加入前綴,如
__declspec(dllexport) int Fun(int a, int b)
否則在加載該dll時會提示找不到該符號
在windows下可以通過vs自帶的dumpbin工具查看可被調(diào)用符號
dumpbin /exports test.dll
C函數(shù)在調(diào)用過程中關(guān)于參數(shù)傳遞和壓棧由多種規(guī)定,作為dll提供給其他程序調(diào)用時,必須明確并統(tǒng)一為同一種調(diào)用規(guī)定,否則會導致棧破壞,編譯器負責具體實現(xiàn)調(diào)用規(guī)定,主要有以下幾種調(diào)用規(guī)定
python下調(diào)用C庫有多種方式,ctypes是其中一種比較方便的,調(diào)用時首先需要加載dll文件,根據(jù)C dll的調(diào)用規(guī)定不同需要使用不同接口,使用ctypes需要 import ctypes 庫
對于簡單的C函數(shù),例如 int add(int a, int b) , 此時就可以直接調(diào)用了,如
對于較復雜的C函數(shù)的參數(shù)情況,ctypes調(diào)用時對入?yún)⒑统霾妥鲆欢ㄌ幚?,這里分情況討論
以上包含了幾種主要的參數(shù)傳遞情況,ctypes也提供了一個較為完整的python類型和C類型的對照,如下:
ctypes:? 可直接調(diào)用c語言動態(tài)鏈接庫。
使用步驟:
1 編譯好自己的動態(tài)連接庫
2 利用ctypes載入動態(tài)連接庫
3 用ctype調(diào)用C函數(shù)接口時,需要將python變量類型做轉(zhuǎn)換后才能作為函數(shù)參數(shù),轉(zhuǎn)換原則見下圖:
4 Python若想獲取ctypes調(diào)用的C函數(shù)返回值,需要先指定返回值類型。我們將在接下來的完整Sample中看到如何使用。
#Step?1:??test.c#include?stdio.h
int?add(int?a,?int?b)
{
return?a?+?b;
}#Step?2:?編譯動態(tài)鏈接庫?(?如何編譯動態(tài)鏈接庫在本文不詳解,網(wǎng)上資料一大堆。)gcc?-fPIC?-shared?test.c?-o?libtest.so??
#Step?3:??test.py
from?ctypes?import?*mylib?=?CDLL("libtest.so")???或者???cdll.LoadLibrary("libtest.so")???add?=?mylib.add
add.argtypes?=?[c_int,?c_int]?#?參數(shù)類型,兩個int(c_int是ctypes類型,見上表)
add.restype?=?c_int?#?返回值類型,int?(c_int?是ctypes類型,見上表)
sum?=?add(3,?6)
分享題目:python和c接口函數(shù),python與C語言
網(wǎng)址分享:http://sd-ha.com/article4/hdheie.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供品牌網(wǎng)站制作、網(wǎng)站內(nèi)鏈、動態(tài)網(wǎng)站、做網(wǎng)站、搜索引擎優(yōu)化、網(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)