python調用c程序

python調用c程序

python語言可以調用c程序,其處理的基本流程如下:

創建c程序功能代碼

一、創建c源程序文件py_test.c

這是程序的具體功能代碼,也就是python需要調用的c源程序。

示例代碼寫了三個方法,最終的效果是python可以調用這三個方法。

/* * File : py_test.c * * Change Logs: * Date Author Notes * 2018-09-22 dolphin the first version */#include<stdio.h>#include<stdlib.h>#include<string.h>/* 求階乘的函數 */int fac(int n){ if(n < 2) return 1; return n*fac(n-1);}/* 字元串逆序的函數 *//* 比如:輸入abcdefg,返回gfedcba */char *reverse(char *s) { char t,*p = s ,*q = (s+strlen(s)-1); while(s && (p<q)) { t = *p; *p++ = *q; *q-- = t; } return s;}/* 用公式計算pi的函數,對應公式如下: pi/2 = 1+1/3+1/3*2/5 + 1/3*2/5*3/7 + 1/3*2/5*3/7*4/9+......*/char *pi_fun(int len, char *ans){ int i; //len為小數長度 int numberator = 1,denominator = 3,result,carry; int flag = 1,count = 0; //繼續循環的標誌及循環的次數 char *pi,*temp; //指向保存pi值和臨時計算結果的數據 len += 2; //增加兩位 printf("len = %d
",len); pi = (char*)malloc(sizeof(char)*(len+1)); //分配保存pi值的內存 temp = (char*)malloc(sizeof(char)*len); //分配保存呢臨時值的內存 for(i = 0; i < len; i++) //初始化數組 { pi[i] = temp[i] = 0; } pi[1] = 2; //置初值 temp[1] = 2; while(flag && (++count < 2147483647)) //int的最大值 2147483647 { carry = 0; for(i = len-1; i > 0; i--) //從低位到高位相乘 { result = temp[i] * numberator + carry; //用每一位去乘,再加上進位 temp[i] = result % 10; //保存個數 carry = result / 10; //進位 } carry = 0; for(i = 0; i < len; i++) //有高位到低位進行除法運算 { result = temp[i] + carry*10; //當前位加上前一位的餘數 temp[i] = result / denominator; //當前位的整數部分 carry = result % denominator; //當前位的餘數,累加到下一位的運算 } flag = 0; //清除標誌 for(i = len-1; i > 0; i--) { result = pi[i] + temp[i]; //將計算結果累加到result中 pi[i] = (result % 10); //保留一位 pi[i-1] += result / 10; //向高位進位 flag |= temp[i]; //若temp中的數全為0,退出循環 } numberator++; //累加分子 denominator += 2; //累加分母 } for(i = 0; i < len; i++) { pi[i] = pi[i] + 0; } pi[0]= pi[1]; pi[1]= .; pi[len] = ; printf("
計算了%d次
",count); //輸出循環次數 strcpy(ans, pi); free(pi); free(temp); return ans;}/* test函數 */int test(void){ return 0;}

二、創建.h頭文件py_test.h

完成功能函數的頭文件,聲明了py_test.c中的功能函數,方便調用。

#ifndef PY_TEST_H_#define PY_TEST_H_char *pi_fun(int len, char *ans);int fac (int n) ;char *reverse(char *s) ;int test(void) ;#endif

python類型適配

一、寫wrapper文件py_testwrapper.c

這個wrapper文件也是一段c語言代碼,可以參考官方的文件Extending Python with C or C++,步驟如下:

1、包含Python.h這個頭文件,這個頭文件在python安裝目錄下的include目錄下找到。

2、包含py_test.h這個頭文件,這個就是創建的功能函數的頭文件。

3、給py_test.c這個源程序中的每個方法都要設置一個wrapper函數,它以PyObject為返回值,並且每個函數都有兩個參數PyObject *self和PyObject *args,用來傳入數據。

4、添加PyMethodDef,用來定義方法名以及方法名與wrapper函數的對應關係。

5、添加PyModuleDef,用來定義模塊名和該模塊所具有的方法。

6、在PyInit_fun中通過PyModule_Create函數創建模塊。

下面就是這個wrapper文件的完整代碼:

/* * File : py_testwrapper.c * * Change Logs: * Date Author Notes * 2018-09-22 dolphin the first version */#include <Python.h>#include <stdlib.h>#include <string.h>#include "py_test.h"/**int PyArg_ParseTuple(PyObject *arg, const char *format, ...);**//* fac功能的wrapper函數 */static PyObject *py_test_fac(PyObject *self, PyObject *args){ int num; int ans; PyObject *retval; //int ok = PyArg_ParseTuple(args, ""); /* No arguments */ /* Python call: f() */ //int ok = PyArg_ParseTuple(args, "s", &s); /* A string */ /* Python call: f(whoops!) */ //int ok = PyArg_ParseTuple(args, "(ii)s#", &i, &j, &s, &size);/* A pair of ints and a string, whose size is also returned */ /* Python call: f((1, 2), three) */ //const char *file; //const char *mode = "r"; //int bufsize = 0; //int ok = PyArg_ParseTuple(args, "s|si", &file, &mode, &bufsize);/* A string, and optionally another string and an integer */ /* Python calls:f(spam)、f(spam, w)、f(spam, wb, 100000) */ //int left, top, right, bottom, h, v; //int ok = PyArg_ParseTuple(args, "((ii)(ii))(ii)",&left, &top, &right, &bottom, &h, &v); /* A rectangle and a point */ /* Python call:f(((0, 0), (400, 300)), (10, 10))*/ //Py_complex c; //int ok = PyArg_ParseTuple(args, "D:myfunction", &c); /* a complex, also providing a function name for errors */ /* Python call: myfunction(1+2j) */ //按整形"i"獲得傳入的整形數據,存入num if (!PyArg_ParseTuple(args,"i",&num)) return NULL; //調用fac,求階乘 ans = fac(num); //按整形"i"將結果裝入retval retval = (PyObject *)Py_BuildValue("i", ans); return retval;}/* reverse功能的wrapper函數,因python中有reverse函數,使用doppel替代 */static PyObject *py_test_doppel(PyObject *self, PyObject *args){ char *src; char *mstr; PyObject *retval; //按字元串"s"獲得傳入的整形數據,存入src if (!PyArg_ParseTuple(args,"s",&src)) return NULL; //申請存儲空間 mstr = malloc(strlen(src) + 1); //拷貝src到mstr strcpy(mstr,src); //調用reverse方法,逆序字元串 reverse(mstr); //按字元串"ss"將兩個字元串裝入retval retval = (PyObject *)Py_BuildValue("ss",src,mstr); //釋放空間 free(mstr); return retval;}/* pi功能的wrapper函數 */static PyObject *py_test_pi(PyObject *self, PyObject *args){ char *mstr; int num ; //int result; PyObject *retval; //按整形"i"獲得傳入的整形數據,存入num if (!PyArg_ParseTuple(args,"i",&num)) return NULL; //申請存儲空間 mstr = (char*)malloc(sizeof(char)*(num + 3)); //調用pi_fun方法 pi_fun(num, mstr); //按字元串"s"將結果裝入retval retval = (PyObject *)Py_BuildValue("s",mstr); //釋放空間 free(mstr); return retval;}/* test功能的wrapper函數 */static PyObject *py_test_test(PyObject *self,PyObject *args){ PyObject *retval; //調用test方法 test(); retval = (PyObject *)Py_BuildValue(""); return retval;}/* 將上述封裝的wrapper函數添加到PyMethodDef中 */static PyMethodDef py_testMethods[] = { {"fac",py_test_fac,METH_VARARGS}, {"doppel",py_test_doppel,METH_VARARGS}, {"test",py_test_test,METH_VARARGS}, {"pi",py_test_pi,METH_VARARGS}, {NULL,NULL},};#if 0/* python2對應的初始化python模塊的方法 */void initpy_test(void){ Py_InitModule("py_test",py_testMethods);}#else/* python3對應的初始化python模塊的方法 */static struct PyModuleDef pytestmodule = { PyModuleDef_HEAD_INIT, "py_test", /* name of module */ NULL, /* module documentation, may be NULL */ -1, /* size of per-interpreter state of the module, or -1 if the module keeps state in global variables. */ py_testMethods};/* The initialization function must be named PyInit_name(), where name is the name of the module, and should be the only non-static item defined in the module file */PyMODINIT_FUNC PyInit_py_test(void){ return PyModule_Create(&pytestmodule);}#endif

編譯、安裝

一、編譯

創建setup.py文件,以便使用python的自帶模塊進行

#incoding:utf-8from distutils.core import setup,Extension#模塊名MOD = py_test#資源(要編譯和鏈接的代碼文件)source = [py_test.c,py_testwrapper.c]#調用setup函數,編譯和鏈接setup(name=MOD,ext_modules=[Extension(MOD,sources=source)])

打開終端命令行窗口,進入文件目錄,輸入「python3 setup.py build」來編譯(可能需要根據提示安裝對應操作系統的工具鏈)。

二、安裝

編譯完成過後,當前文件目錄會出現「build」文件夾,裡面就是編譯後的內容。

編譯完成後我們還要將它們安裝到python的庫中,可以通過命令「sudo python3 setup.py install」來安裝。

即將可執行的功能模塊安裝到了python庫中,在python安裝目錄下Lib目錄的site-packages文件夾下。

以後便可通過「import」將模塊導入,使用模塊中的具體功能。

測試

  以下在python中來調用我們自己的c程序進行測試,測試程序testpython.py如下:

#incoding:utf-8import py_testprint("-"*50)print(py_test.fac(10)) #調用fac()求階乘的函數print(py_test.doppel("This is my world"))#調用逆序函數print("-"*50)

運行程序"python3 testpython.py",程序將運算的結果列印在終端窗口中:

我們有一個用C語言實現的運算圓周率任意位小數的模塊,為了比較運算速度,用python實現同樣功能的python模塊"pi_fun.py"如下,其中py3是與C語言完全對應的python函數,其它兩種演算法("pi"和"pi2")會涉及大數運算,並未直接拿來比較:

import timeimport numpy as npclass pi_fun(object): def pi(places=10): # 3 + 3*(1/24) + 3*(1/24)*(9/80) + 3*(1/24)*(9/80)*(25/168) # The numerators 1, 9, 25, ... are given by (2x + 1) ^ 2 # The denominators 24, 80, 168 are given by (16x^2 -24x + 8) extra = 8 one = 10 ** (places+extra) t, c, n, na, d, da = 3*one, 3*one, 1, 0, 0, 24 while t > 1: n, na, d, da = n+na, na+8, d+da, da+32 t = t * n // d c += t return c // (10 ** extra) def pi_t(n=10): #t1 = time.ticks_us() t = pi(n) #t2 = time.ticks_us() #print(elapsed: , time.ticks_diff(t2,t1)/1000000, s) return t def pi2(n=10): r = 6 * (10 ** n) * 1000 p = 0 k = 0 c = r // 2 d = c // (2 * k + 1) while d > 0: p = p + d k = k + 1 k2 = 2 * k c = c * (k2 - 1) // (4 * k2) d = c // (k2 + 1) return p // 1000 def pi2_t(n=10): #t1 = time.ticks_us() t = pi2(n) #t2 = time.ticks_us() #print(elapsed: , time.ticks_diff(t2,t1)/1000000, s) return t def pi3(n =10): numberator = 1 denominator = 3 result = 0 carry = 0 flag = 1 count = 0 len = n + 2; #增加兩位 pi = [0 for x in range(0, len+1)]#(char*)malloc(sizeof(char)*(len+1)); //分配保存pi值的內存 temp = [0 for x in range(0, len+1)]#(char*)malloc(sizeof(char)*len); //分配保存呢臨時值的內存 pi[1] = 2;#置初值 temp[1] = 2; while flag > 0 and count < 2147483647: #int的最大值 2147483647 count = count + 1 carry = 0; for j in range(0, len-1): #for i = len-1; i > 0; i--) #從低位到高位相乘 result = temp[len -1 - j] * numberator + carry; #用每一位去乘,再加上進位 temp[len - 1 - j] = result % 10; #保存個數 carry = result // 10; #進位 carry = 0; for i in range(0, len): #for(i = 0; i < len; i++) #有高位到低位進行除法運算 result = temp[i] + carry * 10; #當前位加上前一位的餘數 temp[i] = result // denominator; #當前位的整數部分 carry = result % denominator; #當前位的餘數,累加到下一位的運算 flag = 0; #清除標誌 for j in range(0, len-1): #for(i = len-1; i > 0; i--) #{ result = pi[len - 1 - j] + temp[len - 1 - j]; #將計算結果累加到result中 pi[len - 1 - j] = (result % 10); #保留一位 pi[len - 1 - j - 1] = pi[len - 1 - j - 1] + result // 10; #向高位進位 flag = flag | temp[len - 1 - j]; #若temp中的數全為0,退出循環 #} numberator = numberator + 1#累加分子 denominator = denominator + 2#累加分母 pi_ans = pi_ans += str(pi[1]) pi_ans += . for i in range(2, len): pi_ans += str(pi[i]) return pi_ans# if __name__ == "__main__": try: cal = pi_fun print(cal.pi3(10)) finally: print(end fun)

接下來就是為測試函數增加運算時間計算的顯示,修改的"testpython.py"代碼如下:

#incoding:utf-8import py_testimport timeimport numpy as npfrom pi_fun import pi_funif __name__ == "__main__": print("-"*50) print(py_test.fac(10)) #調用fac()求階乘的函數 print(py_test.doppel("This is my world"))#調用逆序函數 start = time.time() k = py_test.pi(1000) t1 = time.time() - start print(t1) print(k) print("-"*50) cal = pi_fun start = time.time() k = cal.pi3(1000) t2 = time.time() - start print(t2) print(k) print("-"*50) print(t2/t1)

運算結果如圖所示,兩者運算1000位圓周率小數結果完全一樣,但消耗的時間有56倍差異:


推薦閱讀:

為什麼大家都想進入IT行業?看這裡就知道了....
簡明例析蒙特卡洛(Monte Carlo)抽樣方法
深入理解計算機系統(二十三):優化程序性能
c#常用編程方法
UG編程補爛面技巧,如何快速把片體變成實體呢?

TAG:編程 | 科技 | 程序 |