之前介紹 python 調用C語言的方式主要有以下3種:
1.通過python的ctypes模塊,調用預編好的C語言動態鏈接庫中的C語言函數。(適合測試)
2.python執行過程中調用(執行)C語言可執行程序。
3.利用模板包裝C語言函數,生成Python擴展模塊,被python直接調用(正式用法,但比較複雜)
這裏先介紹第3種 Python 的C擴展模塊 用法:
@1 用C語言寫一個求n的階乘的函數
#include <stdio.h>
// @ 功能函數
int fact(int n)
{
if (n <= 1){
return 1;
}else{
return n * fact(n-1) ;
}
}
//int main(){
// printf("5! = %d \n",fact(5));
// return 0;
//}
@2 實現爲 python 的模塊
#include <Python.h>
// @ 3.導出函數
PyObject* wrap_fact(PyObject* self, PyObject* args) // 所有的導出函數都具有相同的函數原型:PyObject* method(PyObject* self, PyObject* args);
{
int n, result;
if (! PyArg_ParseTuple(args, "i:fact", &n))
return NULL;
result = fact(n);
return Py_BuildValue("i", result);
}
// @ 2.方法列表
static PyMethodDef exampleMethods[] =
{
{"fact", wrap_fact, METH_VARARGS, "Caculate N!"}, // 方法名:fact,
//導出函數:wrap_fact,
//參數傳遞方式(METH_VARARGS通過Python的元組在Python解釋器和C函數之間傳遞參數;
// METH_KEYWORD通過Python的字典類型在Python解釋器和C函數之間進行參數傳遞)
//方法描述:"Caculate N!"
{NULL, NULL}
};
// @ 1.初始化函數
// Python解釋器規定所有的初始化函數的函數名都必須以init開頭,並加上模塊的名字
void initexample()
{
PyObject* m;
m = Py_InitModule("example", exampleMethods); // 模塊名example,方法列表:初始化函數
}
/*
當Python解釋器需要導入該模塊時,將根據該模塊的名稱查找相應的初始化函數,一旦找到則調用該函數進行相應的初始化工作,初
始化函數則通過調用Python的C語言擴展接口所提供的函數Py_InitModule(),來向Python解釋器註冊該模塊中所有可以用到的方法。
*/
@ 4.編譯鏈接
// 要在Python解釋器中使用C語言編寫的擴展模塊,必須將其編譯成動態鏈接庫的形式。
/*
預編譯,生成目標文件
gcc -fPIC -c -I /usr/include/python2.7/ -I /usr/lib/python2.7/config-x86_64-linux-gnu/ example.c wrap.c
編譯生成庫文件:
gcc -shared -o example.so example.o wrap.o
也可直接編譯成庫:
gcc -o example.so -shared -fPIC -I /usr/include/python2.7/ -I /usr/lib/python2.7/config-x86_64-linux-gnu/ example.c wrap.c
注意:1)編譯生成的庫(.so)名必須和模塊名一致;
2)庫(.so)的位置要在py文件同級下,或存放到python默認的庫的路徑下(例如:/usr/local/lib/python2.7/dist-packages)
3)使用python 的setup方法可以創建庫文件或直接安裝模塊。
*/
@ 4+ setup.py的使用
#!/usr/bin/env python
# -*- coding: utf-8 -*-
'''
使用setup生成庫文件:python setup.py build
使用setup安裝模塊:sudo python setup.py install(將庫文件安裝到:/usr/local/lib/python2.7/dist-packages)
'''
from distutils.core import setup, Extension
if __name__ == "__main__":
setup(name = 'example', \
version = '1.0',\
ext_modules = [Extension('example', ['src/wrap.c','src/example.c'])] \
)
print "done"
'''
重點1:
Extension('example', ['../src/wrap.c','../src/example.c']) 這樣是有問題的,在編譯時可能會出錯:
“
致命錯誤: can't create build/temp.linux-x86_64-2.7/../src/wrap.o: 沒有那個文件或目錄
error: command 'x86_64-linux-gnu-gcc' failed with exit status 1
”
原因是預編譯時生成的目標文件會按照Extension中源文件的樣式產生,所以../的存在會導致路徑出錯。
所以最好是先創建一個鏈接到../src的軟鏈接在本級目錄下。
'''
'''
重點2:
將模塊安裝在指定目錄
python setup.py install --home=<mydir>
使用時需要修改python的搜索路徑:
import sys
sys.path.append('<mydir>')
或在/usr/local/lib/python2.7/dist-packages下創建.pth文件如:example.pth
(import sys;sys.path.append('/home/zhanglu/workspace/PycharmProjects/lib/example/function/python_call_C_CPP/pyObject/easyTest/build/install/lib/python');)
'''
@ 5 在python中調用C擴展模塊
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#========================================================================
# 參考:
# https://www.cnblogs.com/zhangxian/articles/4587770.html
# https://www.cnblogs.com/apexchu/p/5015961.html
#
#=========================================================================
## python 實現求n的階乘
# def fun(x):
# if x == 1:
# return 1
# else:
# return x * fun(x-1)
# pass
#
# if __name__ == "__main__" :
# print fun(5)
## 引用 C 擴展模塊
import sys
## 如果沒有在/usr/local/lib/python2.7/dist-packages下配置pth文件,下面sys.path.append內容需要解注
sys.path.append('./easyTest/lib')
import example
if __name__ == "__main__":
print "Easy test one —— Factorial: (5!) =",example.fact(5)
'''
輸出:
Easy test one —— Factorial: (5!) = 120
'''
@ 6 關於python和C/C++之間數據類型轉換
========================================================================================================================
Py_BuildValue("<format code>",value,value,...)
------------------------------------------------------------------------------------------------------------------------
基礎:
<format code> Python Type C/C++ Type
s str char*
z str/None char*/NULL
i int int
l long long
c str char
d float double
D complex Py_Complex*
O (any) PyObject*
S str PyStringObject
舉例:
Py_BuildValue("") None
Py_BuildValue("i",12) 12
Py_BuildValue("ii",1,2) (1,2)
Py_BuildValue("s","hello") 'hello'
Py_BuildValue("ss","one","two") ('one','two')
Py_BuildValue("s#",abcdef,4) 'abcd'
Py_BuildValue("()") ()
Py_BuildValue("(i)",123) (123)
Py_BuildValue("[i,i]",123,3) [123,4]
Py_BuildValue("{s:i,s:i}","zhang",5,"san",3) {"zhang":5,"san":3}
========================================================================================================================
Python定義了六種數據類型:整型、浮點型、字符串、元組、列表和字典,在使用C語言對Python進行功能擴展時,首先要了解如何在C和Python的
數據類型間進行轉化。
1 整型、浮點型和字符串
在Python的C語言擴展中要用到整型、浮點型和字符串這三種數據類型時相對比較簡單,只需要知道如何生成和維護它們就可以了。下面的例子給出了
如何在C語言中使用Python的這三種數據類型:
例2:typeifs.c
// build an integer
PyObject* pInt = Py_BuildValue("i", 2003);
assert(PyInt_Check(pInt));
int i = PyInt_AsLong(pInt);
Py_DECREF(pInt);
// build a float
PyObject* pFloat = Py_BuildValue("f", 3.14f);
assert(PyFloat_Check(pFloat));
float f = PyFloat_AsDouble(pFloat);
Py_DECREF(pFloat);
// build a string
PyObject* pString = Py_BuildValue("s", "Python");
assert(PyString_Check(pString);
int nLen = PyString_Size(pString);
char* s = PyString_AsString(pString);
Py_DECREF(pString);
2 元組
Python語言中的元組是一個長度固定的數組,當Python解釋器調用C語言擴展中的方法時,所有非關鍵字(non-keyword)參數都以元組方式進行傳遞。
下面的例子示範瞭如何在C語言中使用Python的元組類型:
例3:typetuple.c
// create the tuple
PyObject* pTuple = PyTuple_New(3);
assert(PyTuple_Check(pTuple));
assert(PyTuple_Size(pTuple) == 3);
// set the item
PyTuple_SetItem(pTuple, 0, Py_BuildValue("i", 2003));
PyTuple_SetItem(pTuple, 1, Py_BuildValue("f", 3.14f));
PyTuple_SetItem(pTuple, 2, Py_BuildValue("s", "Python"));
// parse tuple items
int i;
float f;
char *s;
if (!PyArg_ParseTuple(pTuple, "ifs", &i, &f, &s))
PyErr_SetString(PyExc_TypeError, "invalid parameter");
// cleanup
Py_DECREF(pTuple);
3 列表
Python語言中的列表是一個長度可變的數組,列表比元組更爲靈活,使用列表可以對其存儲的Python對象進行隨機訪問。下面的例子示範瞭如何在C語
言中使用Python的列表類型:
例4:typelist.c
// create the list
PyObject* pList = PyList_New(3); // new reference
assert(PyList_Check(pList));
// set some initial values
for(int i = 0; i < 3; ++i)
PyList_SetItem(pList, i, Py_BuildValue("i", i));
// insert an item
PyList_Insert(pList, 2, Py_BuildValue("s", "inserted"));
// append an item
PyList_Append(pList, Py_BuildValue("s", "appended"));
// sort the list
PyList_Sort(pList);
// reverse the list
PyList_Reverse(pList);
// fetch and manipulate a list slice
PyObject* pSlice = PyList_GetSlice(pList, 2, 4); // new reference
for(int j = 0; j < PyList_Size(pSlice); ++j) {
PyObject *pValue = PyList_GetItem(pList, j);
assert(pValue);
}
Py_DECREF(pSlice);
// cleanup
Py_DECREF(pList);
4 字典
Python語言中的字典是一個根據關鍵字進行訪問的數據類型。下面的例子示範瞭如何在C語言中使用Python的字典類型:
例5:typedic.c
// create the dictionary
PyObject* pDict = PyDict_New(); // new reference
assert(PyDict_Check(pDict));
// add a few named values
PyDict_SetItemString(pDict, "first",
Py_BuildValue("i", 2003));
PyDict_SetItemString(pDict, "second",
Py_BuildValue("f", 3.14f));
// enumerate all named values
PyObject* pKeys = PyDict_Keys(); // new reference
for(int i = 0; i < PyList_Size(pKeys); ++i) {
PyObject *pKey = PyList_GetItem(pKeys, i);
PyObject *pValue = PyDict_GetItem(pDict, pKey);
assert(pValue);
}
Py_DECREF(pKeys);
// remove a named value
PyDict_DelItemString(pDict, "second");
// cleanup
Py_DECREF(pDict);