Python3調(diào)用C程序(超詳解)

Python3調(diào)用C程序(超詳解)

Python為什么要調(diào)用C丢氢?

1.要提高代碼的運算速度扔字,C比Python快50倍以上

2.對于C語言里很多傳統(tǒng)類庫变抽,不想用Python重寫哀澈,想對從內(nèi)存到文件接口這樣的底層資源進(jìn)行訪問

Python調(diào)用C的方法:

Python調(diào)用C的方法通常有3種:

1.SWIG,編寫一個額外的接口文件來作為SWIG(終端工具)的入口

2.通過CTypes調(diào)用

3.使用Python/C API方法

第一種方法大多數(shù)情況下會帶來不必要的麻煩卖词,我并沒有試驗,本文只針對2吏夯,3方法作詳細(xì)說明

通過CTypes調(diào)用:

Python中的ctypes模塊可能是Python調(diào)用C方法中最簡單的一種此蜈。ctypes模塊提供了和C語言兼容的數(shù)據(jù)類型和函數(shù)來加載dll文件即横,因此在調(diào)用時不需對源文件做任何的修改。也正是如此奠定了這種方法的簡單性裆赵。

下面是python文件的代碼:

from ctypes import *    #pip ctypes庫令境,并導(dǎo)入庫
test = CDLL("./test.dll")   #調(diào)用當(dāng)前目錄下叫test的dll文件,dll文件是C生成的動態(tài)鏈接庫
result =test.sum(5,10)  #調(diào)用庫里的函數(shù)sum顾瞪,求和函數(shù)
print(result)   #打印結(jié)果

接下來用C語言編寫dll動態(tài)鏈接庫舔庶,這里使用VS:

file

file

單擊頭文件,新建項:

file

添加源文件:

file

在頭文件test.h中加入如下代碼:

#pragma once

#ifdef BUILD_TEST
#define API_SYMBOL __declspec(dllexport)
#else
#define API_SYMBOL __declspec(dllimport)
#endif
//宏定義陈醒,導(dǎo)出或者導(dǎo)入//

extern "C" API_SYMBOL int sum(int x, int y);
//導(dǎo)出函數(shù)//

在源文件test.cpp中加入如下代碼:

#define BUILD_TEST  //使導(dǎo)出函數(shù)生效
#include "test.h"
#include "stdio.h"
#include "pch.h"

int sum(int a, int b) {
    return a + b;
}

注意惕橙,這里要點擊64位,再點擊生成(因為目前大部分電腦安裝Python解釋器是64位的钉跷,否則默認(rèn)生成32位的動態(tài)庫弥鹦,會導(dǎo)致無法調(diào)用)這是一個很隱蔽的坑!R蕖彬坏!

file

在生成的動態(tài)庫路徑下找到test.dll文件,并復(fù)制到python項目下

file
file

最后在python中運行代碼膝晾,出現(xiàn)如下問題:

file

為了尋找問題栓始,我將python文件中的代碼替換如下,發(fā)現(xiàn)調(diào)用動態(tài)庫是成功的血当,只是不能調(diào)用動態(tài)庫里面的函數(shù)

from ctypes import *
from ctypes import *    #pip ctypes庫幻赚,并導(dǎo)入庫
test = CDLL("./test.dll")   #調(diào)用當(dāng)前目錄下叫test的dll文件,dll文件是C生成的動態(tài)鏈接庫

print("加載成功")

# result =test.sum(5,10)    #調(diào)用庫里的函數(shù)sum臊旭,求和函數(shù)
# print(result) #打印結(jié)果
file

以上流程我是參考B站一位UP主的具體教學(xué)落恼,但還是行不通,于是我將test.cpp源文件中的代碼更改如下:

#define BUILD_TEST
#include "test.h"
#include "stdio.h"
#include "pch.h"

#define DLLEXPORT extern "C" __declspec(dllexport)  //直接在源文件定義導(dǎo)出

DLLEXPORT int sum(int a, int b) {
    return a + b;
}//兩數(shù)相加

重復(fù)上述流程离熏,生成dll文件佳谦,將文件放置于python項目中,然后調(diào)用滋戳,終于成功

from ctypes import *
from ctypes import *    #pip ctypes庫钻蔑,并導(dǎo)入庫
test = CDLL("./test.dll")   #調(diào)用當(dāng)前目錄下叫test的dll文件,dll文件是C生成的動態(tài)鏈接庫

print("加載成功")

result =test.sum(5,10)  #調(diào)用庫里的函數(shù)sum胧瓜,求和函數(shù)
print(result)   #打印結(jié)果
file

使用Python/C API方法:

Python/C API可能是被最廣泛使用的方法矢棚。它不僅簡單,而且可以在C代碼中操作你的Python對象府喳。但要使用這種方法蒲肋,需要用特定的方式來編寫C代碼,所以C代碼不是原生的C(大伙要適應(yīng)一下),這樣才可以供python去調(diào)用兜粘。這里參照python進(jìn)階的說明博客園的一篇文章

python文件的代碼如下:

import Test
x = 1
print(Test.add_one(x))

而這里的Test模塊申窘,則是需要我們自己用C語言寫,C文件代碼如下:

#include <Python.h>

int add_one(int a){
    return a + 1;      
}//這是C的原生函數(shù)孔轴,實現(xiàn)+1功能

static PyObject * py_add_one(PyObject *self, PyObject *args){
    int num;
    if (!PyArg_ParseTuple(args, "i", &num)) return NULL;
    return PyLong_FromLong(add_one(num));
}//這一串代碼要實現(xiàn)的功能如下剃法,按照python規(guī)定的調(diào)用方式:
//1.定義一個新的靜態(tài)函數(shù),接收2個PyObject *參數(shù)路鹰,返回1個PyObject *值
//2.PyArg_ParseTuple方法將python輸入的變量變成C的變量贷洲,即上述args→num
//3.緊接著調(diào)用C原生函數(shù)add_one,傳入num
//4.最后將調(diào)用返回的C變量晋柱,轉(zhuǎn)換為PyObject*或其子類优构,并通過PyLong_FromLong方法,返回python值

static PyMethodDef Methods[] = {
    {"add_one", py_add_one, METH_VARARGS}, 
    {NULL, NULL}
};//這串代碼的目的是創(chuàng)建一個數(shù)組雁竞,來指明Python可以調(diào)用這個擴(kuò)展函數(shù):
//其中"add_one"钦椭,代表編譯后python調(diào)用時希望使用的函數(shù)名。而py_add_one碑诉,代表要調(diào)用當(dāng)前C代碼中的哪一個函數(shù)彪腔,即static PyObject * py_add_one。METH_VARARGS进栽,代表函數(shù)的參數(shù)傳遞形式德挣,主要包括位置參數(shù)和關(guān)鍵字參數(shù)兩種
//最后如果希望添加新的函數(shù),則在最后的{NULL, NULL}里按同樣格式填寫新的調(diào)用信息泪幌,比如加一些求和求乘積函數(shù)

static struct PyModuleDef cModule = {
    PyModuleDef_HEAD_INIT,
    "Test", /*模塊名盲厌,即是生成模塊后的名字,如numpy,opencv等等*/
    "", /* 模塊文檔祸泪,可以為空NULL */
    -1, /* 模塊中每個解釋器的狀態(tài)大小,模塊在全局變量中保持狀態(tài)建芙,則為-1 */
    Methods//即上一步定義的Methods没隘,說明了可調(diào)用的函數(shù)集
};//創(chuàng)建module的信息

PyMODINIT_FUNC PyInit_Test(void){ return PyModule_Create(&cModule);}
//module初始化

將C文件放置在python項目的同文件目錄下,然后編寫setup.py文件禁荸,setup.py是用來將C打包成模塊的腳本文件右蒲,代碼如下:

from distutils.core import setup, Extension #這里要用到distutils庫
module1 = Extension('Test', sources = ['test.c']) #打包文件為test.c,這里沒有設(shè)置路徑赶熟,所有.c文件和setup.py放在同一目錄下
setup (name = 'Test', #打包名
       version = '1.0', #版本
       description = 'This is a demo package', #說明文字
       ext_modules = [module1])

然后在pycharm的Terminal終端輸入(因為這里用的python3.5+瑰妄,windows平臺下python的C/C++擴(kuò)展不再支持gcc的編譯,并強(qiáng)制要求使用msvc進(jìn)行編譯映砖,如果版本低于3.5间坐,用python setup.py build這是一個隱藏的坑!!竹宋!用python setup.py build一直出問題

python setup.py build --compiler msvc #編譯代碼
python setup.py build --compiler msvc install #編譯代碼并直接將包放入當(dāng)前python環(huán)境的包的路徑以供調(diào)用
file
file

最后結(jié)果劳澄,順利打包,我們在python里安裝了自己的包并完成調(diào)用:

file
file
file
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末蜈七,一起剝皮案震驚了整個濱河市秒拔,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌飒硅,老刑警劉巖砂缩,帶你破解...
    沈念sama閱讀 211,884評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異三娩,居然都是意外死亡梯轻,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,347評論 3 385
  • 文/潘曉璐 我一進(jìn)店門尽棕,熙熙樓的掌柜王于貴愁眉苦臉地迎上來喳挑,“玉大人,你說我怎么就攤上這事滔悉∫了校” “怎么了?”我有些...
    開封第一講書人閱讀 157,435評論 0 348
  • 文/不壞的土叔 我叫張陵回官,是天一觀的道長曹宴。 經(jīng)常有香客問我,道長歉提,這世上最難降的妖魔是什么笛坦? 我笑而不...
    開封第一講書人閱讀 56,509評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮苔巨,結(jié)果婚禮上版扩,老公的妹妹穿的比我還像新娘。我一直安慰自己侄泽,他們只是感情好礁芦,可當(dāng)我...
    茶點故事閱讀 65,611評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著悼尾,像睡著了一般柿扣。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上闺魏,一...
    開封第一講書人閱讀 49,837評論 1 290
  • 那天未状,我揣著相機(jī)與錄音,去河邊找鬼析桥。 笑死司草,一個胖子當(dāng)著我的面吹牛艰垂,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播翻伺,決...
    沈念sama閱讀 38,987評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼材泄,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了吨岭?” 一聲冷哼從身側(cè)響起拉宗,我...
    開封第一講書人閱讀 37,730評論 0 267
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎辣辫,沒想到半個月后旦事,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,194評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡急灭,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,525評論 2 327
  • 正文 我和宋清朗相戀三年姐浮,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片葬馋。...
    茶點故事閱讀 38,664評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡卖鲤,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出畴嘶,到底是詐尸還是另有隱情蛋逾,我是刑警寧澤,帶...
    沈念sama閱讀 34,334評論 4 330
  • 正文 年R本政府宣布窗悯,位于F島的核電站区匣,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏蒋院。R本人自食惡果不足惜亏钩,卻給世界環(huán)境...
    茶點故事閱讀 39,944評論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望欺旧。 院中可真熱鬧姑丑,春花似錦、人聲如沸切端。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,764評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽踏枣。三九已至,卻和暖如春钙蒙,著一層夾襖步出監(jiān)牢的瞬間茵瀑,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,997評論 1 266
  • 我被黑心中介騙來泰國打工躬厌, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留马昨,地道東北人竞帽。 一個月前我還...
    沈念sama閱讀 46,389評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像鸿捧,于是被迫代替她去往敵國和親屹篓。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,554評論 2 349

推薦閱讀更多精彩內(nèi)容