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

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > python >内容正文

python

python插件开发怎么扩展主程序_python扩展实现方法--python与c混和编程

發(fā)布時間:2025/3/15 python 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 python插件开发怎么扩展主程序_python扩展实现方法--python与c混和编程 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

前言(更新:更方便易用的方式在http://www.swig.org/tutorial.html)

大部分的Python的擴展都是用C語言寫的,但也很容易移植到C++中。

一般來說,所有能被整合或者導(dǎo)入到其它python腳本的代碼,都可以稱為擴展。

擴展可以用純Python來寫,也可以用C或者C++之類的編譯型的語言來擴展。

就算是相同的架構(gòu)的兩臺電腦之間最好也不要互相共享二進制文件,最好在各自的

電腦上編譯Python和擴展。因為就算是編譯器或者CPU之間的些許差異。

官方文檔

需要擴展Python語言的理由:

1. 添加/額外的(非Python)功能,提供Python核心功能中沒有提供的部分,比如創(chuàng)建新的

數(shù)據(jù)類型或者將Python嵌入到其它已經(jīng)存在的應(yīng)用程序中,則必須編譯。

2. 性能瓶頸的效率提升, 解釋型語言一般比編譯型語言慢,想要提高性能,全部改寫成編譯型

語言并不劃算,好的做法是,先做性能測試,找出性能瓶頸部分,然后把瓶頸部分在擴展中實現(xiàn),

是一個比較簡單有效的做法。

3. 保持專有源代碼的私密,腳本語言一個共同的缺陷是,都是執(zhí)行的源代碼,保密性便沒有了。

把一部分的代碼從Python轉(zhuǎn)到編譯語言就可以保持專有源代碼私密性。不容易被反向工程,對涉及

到特殊算法,加密方法,以及軟件安全時,這樣做就顯得很重要。

另一種對代碼保密的方式是只發(fā)布預(yù)編譯后的.pyc文件,是一種折中的方法。

創(chuàng)建Python擴展的步驟

1. 創(chuàng)建應(yīng)用程序代碼

#include?

#include?

#include?

#define?BUFSIZE?10

int?fac(int?n) {

if?(n?

return?1;

return?n?*?fac(n?-?1);

}

char?*reverse(char?*s) {

register?char?t;

char?*p?=?s;

char?*q?=?(s?+?(strlen(s)?-?1));

while?(p?

t?=?*p;

*p++?=?*q;

*q--?=?t;

}

return?s;

}

int?main() {

char?s[BUFSIZE];

printf("4! == %d\n", fac(4));

printf("8! == %d\n", fac(8));

printf("12! == %d\n", fac(12));

strcpy(s,?"abcdef");

printf("reversing 'abcdef', we get '%s'\n", reverse(s));

strcpy(s,?"madam");

printf("reversing 'madam', we get '%s'\n", reverse(s));

return?0;

}

一般是需要寫main()函數(shù),用于單元測試

使用gcc進行編譯

>gcc Extest.c -o Extest

執(zhí)行

>./Extest

2. 利用樣板來包裝代碼

整個擴展的實現(xiàn)都是圍繞"包裝"這個概念來進行的。你的設(shè)計要盡可能讓你的實現(xiàn)語言與Python無縫結(jié)合。

接口的代碼又被稱為"樣板"代碼,它是你的代碼與Python解釋器之間進行交互所必不可少的部分:

我們的樣板代碼分為4步:

a. 包含python的頭文件

需要找到python的頭文件在哪,一般是在/usr/local/include/python2.x中

在上面的C代碼中加入#include "Python.h"

b. 為每個模塊的每一個函數(shù)增加一個型如PyObject* Module_func()的包裝函數(shù)

包裝函數(shù)的用處就是先把python的值傳遞給c,再把c中函數(shù)的計算結(jié)果轉(zhuǎn)換成Python對象返回給python。

需要為所有想被Python環(huán)境訪問到的函數(shù)都增加一個靜態(tài)函數(shù),返回類型為PyObject *,函數(shù)名格式為

模塊名_函數(shù)名;

static?PyObject?*?Extest_fac(PyObject?*self, PyObject?*args) {

int?res;//計算結(jié)果值

int?num;//參數(shù)

PyObject*?retval;//返回值

//i表示需要傳遞進來的參數(shù)類型為整型,如果是,就賦值給num,如果不是,返回NULL;

res?=?PyArg_ParseTuple(args,?"i",?&num);

if?(!res) {

//包裝函數(shù)返回NULL,就會在Python調(diào)用中產(chǎn)生一個TypeError的異常

return?NULL;

}

res?=?fac(num);

//需要把c中計算的結(jié)果轉(zhuǎn)成python對象,i代表整數(shù)對象類型。

retval?=?(PyObject?*)Py_BuildValue("i", res);

return?retval;

}

也可以寫成更簡短,可讀性更強的形式:

static?PyObject?*?Extest_fac(PyObject?*self, PyObject?*args) {

int?m;

if?(!(PyArg_ParseTuple(args,?"i",?&num))) {

return?NULL;

}

return?(PyObject?*)Py_BuildValue("i", fac(num));

}

下面是python和c對應(yīng)的類型轉(zhuǎn)換參數(shù)表:

這里還有一個Py_BuildValue的用法表:

reverse函數(shù)的包裝也類似:

static?PyObject?*

Extest_reverse(PyObject?*self, PyObject?*args) {

char?*orignal;

if?(!(PyArg_ParseTuple(args,?"s",?&orignal))) {

return?NULL;

}

return?(PyObject?*)Py_BuildValue("s", reverse(orignal));

}

也可以再改造成返回包含原始字串和反轉(zhuǎn)字串的tuple的函數(shù)

static?PyObject?*

Extest_doppel(PyObject?*self, PyObject?*args) {

char?*orignal;

if?(!(PyArg_ParseTuple(args,?"s",?&orignal))) {

return?NULL;

}

//ss,就可以返回兩個字符串,應(yīng)該reverse是在原字符串上進行操作,所以需要先strdup復(fù)制一下

return?(PyObject?*)Py_BuildValue("ss", orignal, reverse(strdup(orignal)));

}

上面的代碼有什么問題呢?

和c語言相關(guān)的問題,比較常見的就是內(nèi)存泄露。。。上面的例子中,Py_BuildValue()函數(shù)生成

要返回Python對象的時候,會把轉(zhuǎn)入的數(shù)據(jù)復(fù)制一份。上面的兩個字符串都被復(fù)制出來。但是

我們申請了用于存放第二個字符串的內(nèi)存,在退出的時候沒有釋放掉它。于是內(nèi)存就泄露了。

正確的做法是:先生成返回的python對象,然后釋放在包裝函數(shù)中申請的內(nèi)存。

static?PyObject?*

Extest_doppel(PyObject?*self, PyObject?*args) {

char?*orignal;

char?*reversed;

PyObject?*?retval;

if?(!(PyArg_ParseTuple(args,?"s",?&orignal))) {

return?NULL;

}

retval?=?(PyObject?*)Py_BuildValue("ss", orignal, reversed=reverse(strdup(orignal)));

free(reversed);

return?retval;

}

c. 為每個模塊增加一個型如PyMethodDef ModuleMethods[]的數(shù)組

我們已經(jīng)創(chuàng)建了幾個包裝函數(shù),需要在某個地方把它們列出來,以便python解釋器能夠?qū)氩⒄{(diào)用它們。

這個就是ModuleMethods[]數(shù)組所需要做的事情。

格式如下 ,每一個數(shù)組都包含一個函數(shù)的信息,最后一個數(shù)組放置兩個NULL值,代表聲明結(jié)束

static?PyMethodDef

ExtestMethods[]?=?{

{"fac", Extest_fac, METH_VARARGS},

{"doppel", Extest_doppel, METH_VARARGS},

{"reverse", Extest_reverse, METH_VARARGS},

{NULL, NULL},

};

METH_VARARGS代表參數(shù)以tuple的形式傳入。如果我們需要使用PyArg_ParseTupleAndKeywords()

函數(shù)來分析關(guān)鍵字參數(shù)的話,這個標(biāo)志常量應(yīng)該寫成: METH_VARARGS & METH_KEYWORDS,進行邏輯與運算。

d. 增加模塊初始化函數(shù)void initMethod()

最后的工作就是模塊的初始化工作。這部分代碼在模塊被python導(dǎo)入時進行調(diào)用。

void?initExtest() {

Py_InitModule("Extest", ExtestMethods);

}

最終代碼如下:

#include?

#include?

#include?

#include?"Python.h"

#define?BUFSIZE?10

int?fac(int?n) {

if?(n?

return?1;

return?n?*?fac(n?-?1);

}

char?*reverse(char?*s) {

register?char?t;

char?*p?=?s;

char?*q?=?(s?+?(strlen(s)?-?1));

while?(p?

t?=?*p;

*p++?=?*q;

*q--?=?t;

}

return?s;

}

static?PyObject?*

Extest_fac(PyObject?*self, PyObject?*args) {

int?res;

int?num;

PyObject*?retval;

res?=?PyArg_ParseTuple(args,?"i",?&num);

if?(!res) {

return?NULL;

}

res?=?fac(num);

retval?=?(PyObject?*)Py_BuildValue("i", res);

return?retval;

}

static?PyObject?*

Extest_reverse(PyObject?*self, PyObject?*args) {

char?*orignal;

if?(!(PyArg_ParseTuple(args,?"s",?&orignal))) {

return?NULL;

}

return?(PyObject?*)Py_BuildValue("s", reverse(orignal));

}

static?PyObject?*

Extest_doppel(PyObject?*self, PyObject?*args) {

char?*orignal;

char?*resv;

PyObject?*retval;

if?(!(PyArg_ParseTuple(args,?"s",?&orignal))) {

return?NULL;

}

retval?=?(PyObject?*)Py_BuildValue("ss", orignal, resv=reverse(strdup(orignal)));

free(resv);

return?retval;

}

static?PyMethodDef

ExtestMethods[]?=?{

{"fac", Extest_fac, METH_VARARGS},

{"doppel", Extest_doppel, METH_VARARGS},

{"reverse", Extest_reverse, METH_VARARGS},

{NULL, NULL},

};

void?initExtest() {

Py_InitModule("Extest", ExtestMethods);

}

int?main() {

char?s[BUFSIZE];

printf("4! == %d\n", fac(4));

printf("8! == %d\n", fac(8));

printf("12! == %d\n", fac(12));

strcpy(s,?"abcdef");

printf("reversing 'abcdef', we get '%s'\n", reverse(s));

strcpy(s,?"madam");

printf("reversing 'madam', we get '%s'\n", reverse(s));

test();

return?0;

}

3. 編譯與測試

為了讓你的新python擴展能夠被創(chuàng)建,你需要把它們與python庫放在一起編譯。python中的distutils包被

用來編譯,安裝和分發(fā)這些模塊,擴展和包。步驟如下:

a. 創(chuàng)建setup.py

我們在安裝python第三方包的時候,很多情況下會用到python setup.py install這個命令,

下面我們來了解一下setup.py文件的內(nèi)容。

編譯的最主要的內(nèi)容由setup函數(shù)完成,你需要為每一個擴展創(chuàng)建一個Extension實例,在這里我們只有一個

擴展,所以只需要創(chuàng)建一個實例。

Extension('Extest', sources=['Extest.c']),第一個參數(shù)是擴展的名字,如果模塊是包的一部分,還需要加".";

第二個參數(shù)是源代碼文件列表

setup('Extest', ext_modules=[...]),第一個參數(shù)表示要編譯哪個東西,第二個參數(shù)列出要編譯的Extension對象。

#!/usr/bin/env python

from?distutils.core?import?setup, Extension

MOD?=?'Extest'

setup(name=MOD, ext_modules=[Extension(MOD, sources=['Extest.c'])])

setup函數(shù)還有很多選項可以設(shè)置。詳情可見官網(wǎng)。

b. 通過運行setup.py來編譯和連接你的代碼

在shell中運行命令

>python setup.py build

當(dāng)你報錯如:無法找到Python.h文件

那么說明你沒有安裝python-dev包,需要去官網(wǎng)下載源碼包重裝自己編譯安裝一下python。

Python.h文件一般會出現(xiàn)在/usr/include/Python2.X文件夾中,我這里反正是沒有的。。。

只有重新編譯一個python...

我現(xiàn)在linux系統(tǒng)上的python版本是2.6.6,我下載一個相同版本的源碼,也可以下載更高版本。

解壓源碼包

> tar xzf Python-2.6.6.tgz

> cd Python-2.6.6.tgz

編譯安裝Python

> ./configure --prefix=/usr/local/python2.6

> make

> sudo make install

創(chuàng)建一個新編譯python的鏈接

> sudo ln -sf /usr/local/python2.6/bin/python2.6 /usr/bin/python2.6

測試一下,可用

使用這種方法可以在Linux上運行不同版本的python.

Python.h文件也在/usr/local/python2.6/include/python2.6路徑下找到。

重新運行編譯

編譯成功后,你的擴展就會被創(chuàng)建在bulid/lib.*目錄下。你會看到一個.so文件,這是linux下的

動態(tài)庫文件:

c. 進行調(diào)試

你可以直接用python代碼調(diào)用進行測試:

#!/usr/bin/python

from?ctypes?import?*

import?os

#需要使用絕對路徑

extest?=?cdll.LoadLibrary(os.getcwd()?+?'/Extest.so')

print?extest.fac(4)

也可以在當(dāng)前目錄下執(zhí)行命令,安裝到你的python路徑下

> python setup.py install

安裝成功的話,直接導(dǎo)入測試:

最后需要注意一點的是,原來的c文件中有一個main函數(shù),因為一個系統(tǒng)中只能有一個main

函數(shù),所以為了不起沖突,可以把main函數(shù)改成test函數(shù),再用Extest_test()包裝函數(shù)處理一下,

再加入ExtestMethods數(shù)組,這樣就可以調(diào)用這個測試函數(shù)了。

static?PyObject?*

Extest_test(PyObject?*self, PyObject?*args) {

test();

#返回空的話,就使用下面這一句

return?(PyObject?*)Py_BuildValue("");

}

簡單性能比較

測試代碼

import?Extest

import?time

start?=?time.time()

a?=?Extest.reverse("abcd")

timeC?=?time.time()?-?start

print?'C costs', timeC,?'the result is', a

start?=?time.time()

b?=?list("abcd")

b.reverse()

b?=?''.join(b)

timePython?=?time.time()-start

print?'Python costs', timePython,?'the result is', b

運行結(jié)果

可以看出,python也不是絕對比C慢嘛,還要看情況。

總結(jié)

以上是生活随笔為你收集整理的python插件开发怎么扩展主程序_python扩展实现方法--python与c混和编程的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 日韩制服在线 | 久久久久激情 | 熟妇熟女乱妇乱女网站 | 久久人人草 | 69精品丰满人妻无码视频a片 | 激情吧 | 丰满人妻av一区二区三区 | 日韩欧美一区二区三区四区 | 牛牛视频在线观看 | 精品久久久久久久久久久久久久 | 91一区视频| 精品国产一区二区三区性色av | 美女上床网站 | 在线看片你懂得 | 神马影院午夜伦理 | 国产精品久久久久久婷婷天堂 | 精品视频在线观看 | 欧美丝袜视频 | 小早川怜子一区二区三区 | 青青草视频观看 | 性欧美18—19sex性高清 | 白石茉莉奈中文字幕在 | 日韩成人av影院 | 成人av自拍 | 成人深夜在线观看 | 草比网站 | 国产精品电影在线观看 | 欧美色图片区 | 欧美一区二区最爽乱淫视频免费看 | 中文不卡在线 | 在线你懂 | 18禁裸乳无遮挡啪啪无码免费 | 黑人专干日本人xxxx | 欧美成人免费高清视频 | 最好看的电影2019中文字幕 | 在线爱情大片免费观看大全 | 午夜伦理剧场 | 亲嘴扒胸摸屁股激烈网站 | 嫩草视频在线播放 | 可以免费观看的av | 国产少女免费观看高清 | 91麻豆产精品久久久久久夏晴子 | 中文字幕无码日韩专区免费 | 国产精品免费无遮挡无码永久视频 | 懂色av一区二区三区四区五区 | 亚洲欧美第一 | 欧美日本道 | 国产又黄又猛又爽 | 亚洲性在线 | 精品福利影院 | 伊人色播 | 91香蕉久久| 欧美性受xxx| 色婷婷av一区 | 亚洲精品免费在线观看视频 | 日本不卡一区二区三区在线观看 | 熟女人妻一区二区三区免费看 | 妖精视频在线观看 | avav亚洲 | 久久一| 中文字幕23 | 97在线视频免费观看 | 麻豆一级片| 国产亚洲无码精品 | 日韩毛片免费观看 | 国产精品蜜臀av | 国产香蕉在线观看 | 毛片毛片毛片毛片毛片毛片 | 国产精品久久久久久久免费大片 | 日韩无马| 天天干,夜夜操 | 茄子视频懂你更多在线观看 | 国产丝袜视频在线 | 久久精品大片 | 欧美成人一区二区三区四区 | 国产东北露脸精品视频 | 久久99久 | 日韩欧美中文字幕一区二区 | av日韩av| 婷婷丁香激情 | 久久精品色欲国产AV一区二区 | 成人h动漫精品一区二区下载 | 秋霞午夜鲁丝一区二区老狼 | 99re伊人| 中文字幕一区二区三区乱码在线 | 亚洲综合热 | jizzjizz黄大片 | 超碰在线观看91 | 国产一区二区视频免费观看 | 中文视频在线观看 | 天天狠天天操 | 伊人久久激情 | 亚洲色精品三区二区一区 | 4438x在线观看 | 精品一性一色一乱农村 | 亚洲在线天堂 | www.狠狠操 | 91九色偷拍 | 性三级视频 |