python調C語言的方式——2

之前介紹 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);
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章