1、引言
Python Wraps是Python語言中非常強大的功能,它允許用戶通過使用已經編寫好的代碼,將現有的底層代碼集成在自己編寫的高級代碼中,簡化編程步驟,提高編程效率。本文將從基本概念、代碼實現、使用案例等方面深入淺出地介紹Python Wraps的入門指南。
2、Python Wraps基本概念
1、Python Wraps定義和作用
Python Wraps是指Python中的一個庫,它是Python語言中經典的C/C++擴展方式之一,也是Python中與其他語言編寫的函數互操作的重要工具之一,其主要用途是將C/C++函數或對象封裝成Python模塊。
Python Wraps能夠極大地擴展Python的功能,使得Python語言在使用底層C/C++代碼時方便很多。例如,如果我們想在Python中使用一個性能非常棒的C/C++函數,只需要使用Python Wraps將原始的C/C++函數封裝到Python模塊中,就可以在Python中調用它了,而不必再去寫C/C++的代碼。
2、Python Wraps實現方式
Python Wraps的實現方式有兩種:手動編寫和自動化構建。手動編寫是指將C/C++函數或對象封裝到Python模塊中,需要自己按照Python API編寫底層代碼,使用手動編寫能夠更加靈活,但是需要一定的編程能力;自動化構建則是利用已經存在的工具生成Python Wraps代碼,例如SWIG、Cython等,這種方式雖然不需要編寫Python API,但是有些情況下會導致一些問題。
SWIG(Simplified Wrapper and Interface Generator)可以自動生成支持多種語言的Wraps代碼,包括C/C++、Python、Java、Ruby等。Cython是另一種自動生成Wraps代碼的工具,它可以將Python代碼和C/C++代碼混合編寫。
3、Python Wraps代碼實現
1、手動編寫Python Wraps
下面以一個簡單的例子來介紹手動編寫Python Wraps的過程。
(1)原始的C++函數
int add(int a, int b) { return a + b; }
(2)將C++函數封裝到Python模塊
將C++函數封裝到Python模塊需要使用Python C API編寫代碼,具體代碼如下:
#include <Python.h> static PyObject *add_wrapper(PyObject *self, PyObject *args) { int a, b; if (!PyArg_ParseTuple(args, "ii", &a, &b)) return NULL; return Py_BuildValue("i", add(a, b)); } static PyMethodDef add_methods[] = { {"add", add_wrapper, METH_VARARGS, "Add two integers."}, {NULL, NULL, 0, NULL} }; static struct PyModuleDef add_module = { PyModuleDef_HEAD_INIT, "add", /* 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. */ add_methods }; PyMODINIT_FUNC PyInit_add(void) { return PyModule_Create(&add_module); }
以上代碼中,add_wrapper函數是一個Python函數,它調用了原始的C++函數add,並將計算結果作為Python對象返回;add_methods是一個Python方法,它定義了Python模塊包含的函數;PyModuleDef和PyInit_add是兩個Python C API便利函數,它們可以將Python模塊轉化為C/C++模塊並導出為Python模塊。
2、自動化構建Python Wraps
SWIG和Cython是兩個知名的自動生成Python Wraps的工具。下面分別介紹SWIG和Cython的使用方法。
(1)SWIG工具
SWIG工具可以使用SWIG接口定義語言(Interface Definition Language,IDL)編寫Wraps代碼。SWIG支持多種語言,例如C++, Java, Perl, Python, Ruby, Tcl等。本例中,我們使用SWIG自動生成Python Wraps代碼。
① 安裝SWIG
下載並安裝SWIG。SWIG工具可以從它的官方網站http://www.swig.org/download.html上下載。不同的操作系統有不同的安裝方式,具體安裝過程請參考官方文檔。
② 編寫SWIG文件
在SWIG文件中,需要定義C/C++函數,並使用SWIG標記指定輸入參數、輸出參數以及參數類型。下面是一個簡單的例子,輸出一個整數值:
/* file: example.i */ %module example %{ #include "example.h" %} %include "example.h" int add(int a, int b);
以上的SWIG文件使用了C++頭文件example.h中的add函數,並且添加了一個Python模塊example。函數add是在Python模塊中可用的C++函數。
將以上的SWIG文件example.i保存到一個目錄下。
③ 使用SWIG生成Python Wraps代碼
使用SWIG生成Python Wraps代碼需要保證目錄下有一個example.h文件和上面的example.i文件。在命令行中輸入以下命令:
$ swig -c++ -python example.i
以上命令將會生成一個C++源代碼example_wrap.cxx和一個Python Wraps代碼example.py。其中example_wrap.cxx就是Python Wraps的底層代碼,example.py是Python模塊的使用接口,包含對Python Wraps函數的調用語法。
④ 編譯Python模塊
Python Wraps代碼生成後需要編譯,編譯命令如下:
$ g++ -c -Wall example_wrap.cxx -I/usr/include/python3.6 $ g++ -shared example_wrap.o -o _example.so
在example.py所在的目錄下,執行Python交互環境,便可使用Python模塊example以及其中的函數add了。
(2)Cython工具
Cython工具可以使用Python代碼和C/C++代碼混合編寫Wraps代碼。Cython會將Python代碼轉換成C/C++代碼,使得Python代碼可以訪問底層的C/C++代碼。下面是一個簡單的例子,輸出一個整數值:
### file: example.pyx cdef extern from "example.h": int add(int a, int b) def add_py(int a, int b): cdef int c c = add(a, b) return c
以上代碼使用Cython的extern機制,通過定義C/C++的頭文件example.h,將底層C/C++代碼導入Cython代碼中,然後通過Python定義的高級封裝add_py函數對其封裝,便於在Python代碼中使用。
編譯Python模塊:
$ cython example.pyx $ gcc -shared -pthread -fPIC -fwrapv -O2 -Wall -fno-strict-aliasing -I/usr/include/python3.4m example.c -o example.so
在example.py所在的目錄下,執行Python交互環境,便可使用Python模塊example以及其中的函數add_py了。
4、Python Wraps使用案例
以下是一個簡單的使用Python Wraps的案例,使用numpy庫輔助繪製二維點。該庫使用C++實現的多項式擬合,並使用Python Wraps封裝為Python模塊調用。下面分別介紹手動編寫Python Wraps和自動化構建Python Wraps的實現方式。
1、手動編寫Python Wraps
以下是多項式擬合C++實現的頭文件polyfit.h,以及我們需要封裝成Python模塊的add函數:
#ifndef POLYFIT_H #define POLYFIT_H #include <vector> double polyfit(const std::vector<double> &x, const std::vector<double> &y, int order); int add(int a, int b); #endif
將add函數封裝為Python模塊,代碼如下:
#include <Python.h> #include <numpy/ndarrayobject.h> #include "polyfit.h" static PyObject* py_add(PyObject* self, PyObject* args) { int a, b; if (!PyArg_ParseTuple(args, "ii", &a, &b)) { return NULL; } return Py_BuildValue("i", add(a, b)); } /* Wraps module functions */ static PyMethodDef polyfit_methods[] = { {"add", (PyCFunction) py_add, METH_VARARGS, "add two integers"}, {NULL, NULL, 0, NULL} }; /* Module initialization */ PyMODINIT_FUNC initpolyfit(void) { PyObject *m = Py_InitModule3("polyfit", polyfit_methods, "polyfit module"); import_array(); }
編譯Python模塊:
$ g++ -O3 -Wall -shared -std=c++11 -fPIC `python2.7-config --includes`\ `python2.7-config --libs` -I/usr/include/python2.7\ -o polyfit.so polyfit_wrap.cxx
在Python交互環境中:
import polyfit polyfit.add(1, 2) # 輸出 3
2、自動化構建Python Wraps
為了演示自動化構建Python Wraps,我們使用SWIG完成numpy.polyfit的封裝。以下是使用SWIG封裝後的numpy.polyfit代碼,其核心代碼polyfit.i如下:
%module polyfit %{ #include "polyfit.h" %} %include <numpy.i> %numpy_typemaps(double, NPY_DOUBLE) %apply (double *IN_ARRAY1, int DIM1, double *IN_ARRAY2, int DIM2, int, double *OUT_ARRAY, int DIM3) { (double *x, int x_size, double *y, int y_size, int order, double *coefficients, int coefficients_size) } %{ static PyObject* py_polyfit(PyObject* self, PyObject* args) { PyObject *x_obj, *y_obj; int order; if(!PyArg_ParseTuple(args, "OOi", &x_obj, &y_obj, &order)) { return NULL; } int x_size = PyArray_DIM(x_obj, 0); int y_size = PyArray_DIM(y_obj, 0); npy_double *x = (npy_double*)PyArray_DATA(x_obj); npy_double *y = (npy_double*)PyArray_DATA(y_obj); npy_intp shape[1] = {order + 1}; PyObject* coefficients_object = PyArray_SimpleNew(1, shape, NPY_DOUBLE); npy_double *coefficients = (double*)PyArray_DATA((PyArrayObject*)coefficients_object); polyfit(x, x_size, y, y_size, order, coefficients, order + 1); return coefficients_object; } %} %include "polyfit.h"
SWIG將polyfit.i文件轉換為C++源代碼:
$ swig -c++ -python polyfit.i
將生成的polyfit_wrap.cxx文件編譯為Python模塊:
$ g++ -O3 -Wall -shared -std=c++11 -fPIC `python2.7-config --includes`\
`python2.7-config --libs` -I/usr/include/python2.7\
-o _poly原創文章,作者:小藍,如若轉載,請註明出處:https://www.506064.com/zh-hant/n/183604.html