<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??碼云GVP開源項目 12k star Uniapp+ElementUI 功能強大 支持多語言、二開方便! 廣告
                # 2.8 與C進行交互 In?[1]: ``` %matplotlib inline import numpy as np ``` > **作者**: Valentin Haenel 本章包含了許多可以在Python使用原生代碼(主要是C/C++)方式的_介紹_,這個過程通常被稱為_封裝_。本章的目的是給你有哪些技術存在已經各自有優劣式的一點兒感覺,這樣你可以根據你的具體需求選擇適合的方式。無論如何,只要你開始做封裝,你幾乎都必然需要咨詢你選定技術的文檔。 章節內容 ``` 簡介 Python-C-Api Ctypes SWIG Cython 總結 進一步閱讀和參考 練習 ``` ## 2.8.1 簡介 本章將涵蓋一下技術: * Python-C-Api * Ctypes * SWIG (簡化封裝器和接口生成器) * Cython 這四種技術可能是最知名的,其中Cython可能是最高級的,并且你應該最優先使用它。其他的技術也很重要,如果你想要從不同點角度理解封裝問題。之前提到過,還有其他的替代技術,但是,理解了以上這些基礎的,你就可以評估你選擇的技術是否滿足你的需求。 在評估技術時,下列標準會有幫助: * 需要額外的庫嗎? * 代碼可以自動生成? * 是否需要編譯? * 與Numpy數組交互是否有良好的支持? * 是否支持C++? 在你動手前,應該先考慮一下使用情景。在于原生代碼交互時,通常來自于兩個應用場景: * 需要利用C/C++中現存的代碼,或者是因為它已經存在,或者是因為它更快。 * Python代碼太慢,將內部循環變成原生代碼 每個技術都使用來自math.h的`cos`函數的封裝來進行演示。盡管這是一個無聊例子,但是它確實給我們很好的演示了封裝方法的基礎,因為每個技術也包含了一定程度Numpy支持,這也用計算一些數組來計算consine的例子來演示。 最后,兩個小警示: * 所有這些方法在Python解釋器中都可能崩潰(細分錯誤),因為在C代碼中的錯誤。 * 所有的例子都在Linux中完成,他們應該在其他操作系統中也可用。 * 在大多數例子中,你都會需要C編譯器。 ## 2.8.2 Python-C-Api Python-C-API是標準Python解釋器(即CPython)的基礎。使用這個API可以在C和C++中寫Python擴展模塊。很明顯,由于語言兼容性的優點,這些擴展模塊可以調用任何用C或者C++寫的函數。 當使用Python-C-API時,人們通常寫許多樣板化的代碼,首先解析函數接收的參數,然后構建返回的類型。 **優點** * 不需要額外的庫 * 許多系層的控制 * C++完全可用 **不足** * 可以需要一定的努力 * 高代碼成本 * 必須編譯 * 高維護成本 * 如果C-API改變無法向前兼容Python版本 * 引用計數錯誤很容易出現,但是很難被跟蹤。 **注意** 此處的Python-C-Api例子主要是用來演示。許多其他例子的確依賴它,因此,對于它如何工作有一個高層次的理解。在99%的使用場景下,使用替代技術會更好。 **注意** 因為引用計數很容易出現然而很難被跟蹤,任何需要使用Python C-API的人都應該閱讀[官方Python文檔關于對象、類型和引用計數的部分](https://docs.python.org/2/c-api/intro.html#objects-types-and-reference-counts)。此外,有一個名為[cpychecker](https://gcc-python-plugin.readthedocs.org/en/latest/cpychecker.html)的工具可以發現引用計數的常見錯誤。 ### 2.8.2.1 例子 下面的C擴展模塊,讓來自標準`math`庫的`cos`函數在Python中可用: In?[?]: ``` /* 用Python-C-API封裝來自math.h的cos函數的例子 */ #include <Python.h> #include <math.h> /* wrapped cosine function */ static PyObject* cos_func(PyObject* self, PyObject* args) { double value; double answer; /* parse the input, from python float to c double */ if (!PyArg_ParseTuple(args, "d", &value)) return NULL; /* if the above function returns -1, an appropriate Python exception will * have been set, and the function simply returns NULL */ /* call cos from libm */ answer = cos(value); /* construct the output from cos, from c double to python float */ return Py_BuildValue("f", answer); } /* define functions in module */ static PyMethodDef CosMethods[] = { {"cos_func", cos_func, METH_VARARGS, "evaluate the cosine"}, {NULL, NULL, 0, NULL} }; /* module initialization */ PyMODINIT_FUNC initcos_module(void) { (void) Py_InitModule("cos_module", CosMethods); } ``` 如你所見,有許多樣板,既包括 ?massage? 的參數和return類型以及模塊初始化。盡管隨著擴展的增長,這些東西中的一些是分期償還,模板每個函數需要的模板還是一樣的。 標準python構建系統`distutils`支持從`setup.py`編譯C-擴展, 非常方便: In?[?]: ``` from distutils.core import setup, Extension # 定義擴展模塊 cos_module = Extension('cos_module', sources=['cos_module.c']) # 運行setup setup(ext_modules=[cos_module]) ``` 這可以被編譯: ``` $ cd advanced/interfacing_with_c/python_c_api $ ls cos_module.c setup.py $ python setup.py build_ext --inplace running build_ext building 'cos_module' extension creating build creating build/temp.linux-x86_64-2.7 gcc -pthread -fno-strict-aliasing -g -O2 -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -fPIC -I/home/esc/anaconda/include/python2.7 -c cos_module.c -o build/temp.linux-x86_64-2.7/cos_module.o gcc -pthread -shared build/temp.linux-x86_64-2.7/cos_module.o -L/home/esc/anaconda/lib -lpython2.7 -o /home/esc/git-working/scipy-lecture-notes/advanced/interfacing_with_c/python_c_api/cos_module.so $ ls build/ cos_module.c cos_module.so setup.py ``` * `build_ext` 是構建擴展模塊 * `--inplace` 將把編譯后的擴展模塊輸出到當前目錄 文件`cos_module.so`包含編譯后的擴展,我們可以在IPython解釋器中加載它: In?[?]: ``` In [1]: import cos_module In [2]: cos_module? Type: module String Form:<module 'cos_module' from 'cos_module.so'> File: /home/esc/git-working/scipy-lecture-notes/advanced/interfacing_with_c/python_c_api/cos_module.so Docstring: <no docstring> In [3]: dir(cos_module) Out[3]: ['__doc__', '__file__', '__name__', '__package__', 'cos_func'] In [4]: cos_module.cos_func(1.0) Out[4]: 0.5403023058681398 In [5]: cos_module.cos_func(0.0) Out[5]: 1.0 In [6]: cos_module.cos_func(3.14159265359) Out[7]: -1.0 ``` 現在我們看一下這有多強壯: In?[?]: ``` In [10]: cos_module.cos_func('foo') --------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-10-11bee483665d> in <module>() ----> 1 cos_module.cos_func('foo') TypeError: a float is required ``` ### 2.8.2.2\. Numpy 支持 Numpy模擬Python-C-API, 自身也實現了C-擴展, 產生了Numpy-C-API。這個API可以被用來創建和操作來自C的Numpy數組, 當寫一個自定義的C-擴展。也可以看一下:參考:`advanced_numpy`。 **注意** 如果你確實需要使用Numpy C-API參考關于[Arrays](http://docs.scipy.org/doc/numpy/reference/c-api.array.html)和[Iterators](http://docs.scipy.org/doc/numpy/reference/c-api.iterator.html)的文檔。 下列的例子顯示如何將Numpy數組作為參數傳遞給函數,以及如果使用(舊)Numpy-C-API在Numpy數組上迭代。它只是將一個數組作為參數應用到來自`math.h`的cosine函數,并且返回生成的新數組。 In?[?]: ``` /* 使用Numpy-C-API封裝來自math.h的cos函數 . */ #include <Python.h> #include <numpy/arrayobject.h> #include <math.h> /* 封裝cosine函數 */ static PyObject* cos_func_np(PyObject* self, PyObject* args) { PyArrayObject *in_array; PyObject *out_array; NpyIter *in_iter; NpyIter *out_iter; NpyIter_IterNextFunc *in_iternext; NpyIter_IterNextFunc *out_iternext; /* parse single numpy array argument */ if (!PyArg_ParseTuple(args, "O!", &PyArray_Type, &in_array)) return NULL; /* construct the output array, like the input array */ out_array = PyArray_NewLikeArray(in_array, NPY_ANYORDER, NULL, 0); if (out_array == NULL) return NULL; /* create the iterators */ in_iter = NpyIter_New(in_array, NPY_ITER_READONLY, NPY_KEEPORDER, NPY_NO_CASTING, NULL); if (in_iter == NULL) goto fail; out_iter = NpyIter_New((PyArrayObject *)out_array, NPY_ITER_READWRITE, NPY_KEEPORDER, NPY_NO_CASTING, NULL); if (out_iter == NULL) { NpyIter_Deallocate(in_iter); goto fail; } in_iternext = NpyIter_GetIterNext(in_iter, NULL); out_iternext = NpyIter_GetIterNext(out_iter, NULL); if (in_iternext == NULL || out_iternext == NULL) { NpyIter_Deallocate(in_iter); NpyIter_Deallocate(out_iter); goto fail; } double ** in_dataptr = (double **) NpyIter_GetDataPtrArray(in_iter); double ** out_dataptr = (double **) NpyIter_GetDataPtrArray(out_iter); /* iterate over the arrays */ do { **out_dataptr = cos(**in_dataptr); } while(in_iternext(in_iter) && out_iternext(out_iter)); /* clean up and return the result */ NpyIter_Deallocate(in_iter); NpyIter_Deallocate(out_iter); Py_INCREF(out_array); return out_array; /* in case bad things happen */ fail: Py_XDECREF(out_array); return NULL; } /* 在模塊中定義函數 */ static PyMethodDef CosMethods[] = { {"cos_func_np", cos_func_np, METH_VARARGS, "evaluate the cosine on a numpy array"}, {NULL, NULL, 0, NULL} }; /* 模塊初始化 */ PyMODINIT_FUNC initcos_module_np(void) { (void) Py_InitModule("cos_module_np", CosMethods); /* IMPORTANT: this must be called */ import_array(); } ``` 要編譯這個模塊,我們可以再用`distutils`。但是我們需要通過使用func:numpy.get_include確保包含了Numpy頭部: In?[?]: ``` from distutils.core import setup, Extension import numpy # define the extension module cos_module_np = Extension('cos_module_np', sources=['cos_module_np.c'], include_dirs=[numpy.get_include()]) # run the setup setup(ext_modules=[cos_module_np]) ``` 要說服我們自己這個方式確實有效,我們來跑一下下面的測試腳本: In?[?]: ``` import cos_module_np import numpy as np import pylab x = np.arange(0, 2 * np.pi, 0.1) y = cos_module_np.cos_func_np(x) pylab.plot(x, y) pylab.show() ``` 這會產生以下的圖像: ![](http://www.scipy-lectures.org/_images/test_cos_module_np.png) ## 2.8.3\. Ctypes Ctypes是Python的一個外來函數庫。它提供了C兼容的數據類型,并且允許在DLLs或者共享的庫中調用函數。它可以用來在純Python中封裝這些庫。 **優點** * Python標準庫的一部分 * 不需要編譯 * 代碼封裝都是在Python中 **不足** * 需要代碼作為一個共享的庫(粗略地說,在windows中是 *.dll,在Linux中是*.so,在Mac OSX中是 *.dylib) * 對C++支持并不好 ### 2.8.3.1 例子 如前面提到的,代碼封裝完全在Python中。 In?[?]: ``` """ 用ctypes封裝來自math.h的 cos 函數。 """ import ctypes from ctypes.util import find_library # find and load the library libm = ctypes.cdll.LoadLibrary(find_library('m')) # set the argument type libm.cos.argtypes = [ctypes.c_double] # set the return type libm.cos.restype = ctypes.c_double def cos_func(arg): ''' 封裝math.h cos函數 ''' return libm.cos(arg) ``` * 尋找和加載庫可能非常依賴于你的操作系統,檢查[文檔](https://docs.python.org/2/library/ctypes.html#loading-dynamic-link-libraries)來了解細節 * 這可能有些欺騙性,因為math庫在系統中已經是編譯模式。如果你想要封裝一個內置的庫,需要先編譯它,可能需要或者不需要額外的工作。 我們現在可以像前面一樣使用這個庫: In?[?]: ``` In [1]: import cos_module In [2]: cos_module? Type: module String Form:<module 'cos_module' from 'cos_module.py'> File: /home/esc/git-working/scipy-lecture-notes/advanced/interfacing_with_c/ctypes/cos_module.py Docstring: <no docstring> In [3]: dir(cos_module) Out[3]: ['__builtins__', '__doc__', '__file__', '__name__', '__package__', 'cos_func', 'ctypes', 'find_library', 'libm'] In [4]: cos_module.cos_func(1.0) Out[4]: 0.5403023058681398 In [5]: cos_module.cos_func(0.0) Out[5]: 1.0 In [6]: cos_module.cos_func(3.14159265359) Out[6]: -1.0 ``` ### 2.8.3.2 Numpy支持 Numpy包含一些與ctypes交互的支持。特別是支持將特定Numpy數組屬性作為ctypes數據類型研究,并且有函數可以將C數組和Numpy數據互相轉換。 更多信息,可以看一下Numpy手冊的對應部分或者`numpy.ndarray.ctypes`和`numpy.ctypeslib`的API文檔。 對于下面的例子,讓我們假設一個C函數,輸入輸出都是一個數組,計算輸入數組的cosine并將結果輸出為一個數組。 庫包含下列頭文件(盡管在這個例子中并不是必須這樣,為了完整,我們還是把這一步列出來): In?[?]: ``` void cos_doubles(double * in_array, double * out_array, int size); ``` 這個函數實現在下列的C源文件中: In?[?]: ``` #include <math.h> /* Compute the cosine of each element in in_array, storing the result in * out_array. */ void cos_doubles(double * in_array, double * out_array, int size){ int i; for(i=0;i<size;i++){ out_array[i] = cos(in_array[i]); } } ``` 并且因為這個庫是純C的,我們不能使用`distutils`來編譯,但是,必須使用`make`和`gcc`的組合: In?[?]: ``` m.PHONY : clean libcos_doubles.so : cos_doubles.o gcc -shared -Wl,-soname,libcos_doubles.so -o libcos_doubles.so cos_doubles.o cos_doubles.o : cos_doubles.c gcc -c -fPIC cos_doubles.c -o cos_doubles.o clean : -rm -vf libcos_doubles.so cos_doubles.o cos_doubles.pyc ``` 接下來,我們可以將這個庫編譯到共享的庫 (on Linux)`libcos_doubles.so`: In?[?]: ``` $ ls cos_doubles.c cos_doubles.h cos_doubles.py makefile test_cos_doubles.py $ make gcc -c -fPIC cos_doubles.c -o cos_doubles.o gcc -shared -Wl,-soname,libcos_doubles.so -o libcos_doubles.so cos_doubles.o $ ls cos_doubles.c cos_doubles.o libcos_doubles.so* test_cos_doubles.py cos_doubles.h cos_doubles.py makefile ``` 現在我們可以繼續通過ctypes對Numpy數組的直接支持(一定程度上)來封裝這個庫: In?[?]: ``` """ 封裝一個使用numpy.ctypeslib接受C雙數組作為輸入的例子。""" import numpy as np import numpy.ctypeslib as npct from ctypes import c_int # cos_doubles的輸入類型 # 必須是雙數組, 有相鄰的單維度 array_1d_double = npct.ndpointer(dtype=np.double, ndim=1, flags='CONTIGUOUS') # 加載庫,運用numpy機制 libcd = npct.load_library("libcos_doubles", ".") # 設置反饋類型和參數類型 libcd.cos_doubles.restype = None libcd.cos_doubles.argtypes = [array_1d_double, array_1d_double, c_int] def cos_doubles_func(in_array, out_array): return libcd.cos_doubles(in_array, out_array, len(in_array)) ``` * 注意臨近單維度Numpy數組的固有限制,因為C函數需要這類的緩存器。 * 也需要注意輸出數組也需要是預分配的,例如[numpy.zeros()](http://docs.scipy.org/doc/numpy/reference/generated/numpy.zeros.html#numpy.zeros)和函數將用它的緩存器來寫。 * 盡管`cos_doubles`函數的原始簽名是`ARRAY`, `ARRAY`, `int`最終的`cos_doubles_func`需要兩個Numpy數組作為參數。 并且,和前面一樣,我們我要為自己證明一下它是有效的: In?[?]: ``` import numpy as np import pylab import cos_doubles x = np.arange(0, 2 * np.pi, 0.1) y = np.empty_like(x) cos_doubles.cos_doubles_func(x, y) pylab.plot(x, y) pylab.show() ``` ![](http://www.scipy-lectures.org/_images/test_cos_doubles.png) ## 2.8.4 SWIG [SWIG](http://www.swig.org/), 簡化封裝接口生成器,是一個聯接用C和C++寫的程序與需要高級程序語言,包括Python的軟件開發工具。SWIG的重點在于它可以為你自動生成封裝器代碼。盡管從編程時間上來說這是一個優勢,但是同時也是一個負擔。生成的文件通常很大,并且可能并不是人類可讀的,封裝過程造成的多層間接引用可能很難理解。 **注意** 自動生成的C代碼使用Python-C-Api。 **優勢** * 給定頭部可以自動封裝整個庫 * 在C++中表現良好 **不足** * 自動生成的文件很龐大 * 如果出錯很難debug * 陡峭的學習曲線 ### 2.8.4.1 例子 讓我們想象我們的`cos`函數存在于用C寫的`cos_module`中,包含在源文件`cos_module.c`中: In?[?]: ``` #include <math.h> double cos_func(double arg){ return cos(arg); } ``` 頭文件`cos_module.h`: In?[?]: ``` double cos_func(double arg); ``` 盡管我們的目的是將`cos_func`暴露給Python。要用SWIG來完成這個目的,我們需要寫一個包含SWIG指導的接口文件。 In?[?]: ``` /* Example of wrapping cos function from math.h using SWIG. */ %module cos_module %{ /* the resulting C file should be built as a python extension */ #define SWIG_FILE_WITH_INIT /* Includes the header in the wrapper code */ #include "cos_module.h" %} /* Parse the header file to generate wrappers */ %include "cos_module.h" ``` 如你所見,這里不需要太多的代碼。對于這個簡單的例子,它簡單到只需要在接口文件中包含一個頭文件,來向Python暴露函數。但是,SWIG確實允許更多精細包含或者排除在頭文件中發現的函數,細節檢查一下文檔。 生成編譯的封裝器是一個兩階段的過程: * 在接口文件上運行`swig`可執行文件來生成文件`cos_module_wrap.c`, 其源文件是自動生成的Python C-extension和`cos_module.py`, 是自動生成的Python模塊。 * 編譯`cos_module_wrap.c`到`_cos_module.so`。幸運的,`distutils`知道如何處理SWIG接口文件, 因此我們的`setup.py`是很簡單的: In?[?]: ``` from distutils.core import setup, Extension setup(ext_modules=[Extension("_cos_module", sources=["cos_module.c", "cos_module.i"])]) ``` In?[?]: ``` $ cd advanced/interfacing_with_c/swig $ ls cos_module.c cos_module.h cos_module.i setup.py $ python setup.py build_ext --inplace running build_ext building '_cos_module' extension swigging cos_module.i to cos_module_wrap.c swig -python -o cos_module_wrap.c cos_module.i creating build creating build/temp.linux-x86_64-2.7 gcc -pthread -fno-strict-aliasing -g -O2 -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -fPIC -I/home/esc/anaconda/include/python2.7 -c cos_module.c -o build/temp.linux-x86_64-2.7/cos_module.o gcc -pthread -fno-strict-aliasing -g -O2 -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -fPIC -I/home/esc/anaconda/include/python2.7 -c cos_module_wrap.c -o build/temp.linux-x86_64-2.7/cos_module_wrap.o gcc -pthread -shared build/temp.linux-x86_64-2.7/cos_module.o build/temp.linux-x86_64-2.7/cos_module_wrap.o -L/home/esc/anaconda/lib -lpython2.7 -o /home/esc/git-working/scipy-lecture-notes/advanced/interfacing_with_c/swig/_cos_module.so $ ls build/ cos_module.c cos_module.h cos_module.i cos_module.py _cos_module.so* cos_module_wrap.c setup.py ``` 我們可以像前面的例子中那樣加載和運行`cos_module`: In?[?]: ``` In [1]: import cos_module In [2]: cos_module? Type: module String Form:<module 'cos_module' from 'cos_module.py'> File: /home/esc/git-working/scipy-lecture-notes/advanced/interfacing_with_c/swig/cos_module.py Docstring: <no docstring> In [3]: dir(cos_module) Out[3]: ['__builtins__', '__doc__', '__file__', '__name__', '__package__', '_cos_module', '_newclass', '_object', '_swig_getattr', '_swig_property', '_swig_repr', '_swig_setattr', '_swig_setattr_nondynamic', 'cos_func'] In [4]: cos_module.cos_func(1.0) Out[4]: 0.5403023058681398 In [5]: cos_module.cos_func(0.0) Out[5]: 1.0 In [6]: cos_module.cos_func(3.14159265359) Out[6]: -1.0 ``` 接下來我們測試一下強壯性,我們看到我們可以獲得一個更多的錯誤信息 (雖然, 嚴格來講在Python中沒有double類型): In?[?]: ``` In [7]: cos_module.cos_func('foo') --------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-7-11bee483665d> in <module>() ----> 1 cos_module.cos_func('foo') TypeError: in method 'cos_func', argument 1 of type 'double' ``` ### 2.8.4.2 Numpy 支持 Numpy在`numpy.i`文件中提供了[SWIG的支持](http://docs.scipy.org/doc/numpy/reference/swig.html)。這個接口文件定義了許多所謂的typemaps,支持了Numpy數組和C-Arrays的轉化。在接下來的例子中,我們將快速看一下typemaps實際是如何工作的。 我們有相同的`cos_doubles`函數,在ctypes例子中: In?[?]: ``` void cos_doubles(double * in_array, double * out_array, int size); ``` In?[?]: ``` #include <math.h> /* Compute the cosine of each element in in_array, storing the result in * out_array. */ void cos_doubles(double * in_array, double * out_array, int size){ int i; for(i=0;i<size;i++){ out_array[i] = cos(in_array[i]); } } ``` 使用了SWIG接口文件封裝了`cos_doubles_func`: In?[?]: ``` /* Example of wrapping a C function that takes a C double array as input using * numpy typemaps for SWIG. */ %module cos_doubles %{ /* the resulting C file should be built as a python extension */ #define SWIG_FILE_WITH_INIT /* Includes the header in the wrapper code */ #include "cos_doubles.h" %} /* include the numpy typemaps */ %include "numpy.i" /* need this for correct module initialization */ %init %{ import_array(); %} /* typemaps for the two arrays, the second will be modified in-place */ %apply (double* IN_ARRAY1, int DIM1) {(double * in_array, int size_in)} %apply (double* INPLACE_ARRAY1, int DIM1) {(double * out_array, int size_out)} /* Wrapper for cos_doubles that massages the types */ %inline %{ /* takes as input two numpy arrays */ void cos_doubles_func(double * in_array, int size_in, double * out_array, int size_out) { /* calls the original funcion, providing only the size of the first */ cos_doubles(in_array, out_array, size_in); } %} ``` * 要使用Numpy的typemaps, 我們需要包含`numpy.i`文件。 * 觀察一下對`import_array()`的調用,這個模塊我們已經在Numpy-C-API例子中遇到過。 * 因為類型映射只支持ARRAY、SIZE的簽名,我們需要將cos_doubles封裝為cos_doubles_func,接收兩個數組包括大小作為輸入。 * 與SWIG不同的是, 我們并沒有包含`cos_doubles.h`頭部,我們并不需要暴露給Python,因為,我們通過`cos_doubles_func`暴露了相關的功能。 并且,和之前一樣,我們可以用`distutils`來封裝這個函數: In?[?]: ``` from distutils.core import setup, Extension import numpy setup(ext_modules=[Extension("_cos_doubles", sources=["cos_doubles.c", "cos_doubles.i"], include_dirs=[numpy.get_include()])]) ``` 和前面一樣,我們需要用`include_dirs`來制定位置。 In?[?]: ``` $ ls cos_doubles.c cos_doubles.h cos_doubles.i numpy.i setup.py test_cos_doubles.py $ python setup.py build_ext -i running build_ext building '_cos_doubles' extension swigging cos_doubles.i to cos_doubles_wrap.c swig -python -o cos_doubles_wrap.c cos_doubles.i cos_doubles.i:24: Warning(490): Fragment 'NumPy_Backward_Compatibility' not found. cos_doubles.i:24: Warning(490): Fragment 'NumPy_Backward_Compatibility' not found. cos_doubles.i:24: Warning(490): Fragment 'NumPy_Backward_Compatibility' not found. creating build creating build/temp.linux-x86_64-2.7 gcc -pthread -fno-strict-aliasing -g -O2 -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -fPIC -I/home/esc/anaconda/lib/python2.7/site-packages/numpy/core/include -I/home/esc/anaconda/include/python2.7 -c cos_doubles.c -o build/temp.linux-x86_64-2.7/cos_doubles.o gcc -pthread -fno-strict-aliasing -g -O2 -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -fPIC -I/home/esc/anaconda/lib/python2.7/site-packages/numpy/core/include -I/home/esc/anaconda/include/python2.7 -c cos_doubles_wrap.c -o build/temp.linux-x86_64-2.7/cos_doubles_wrap.o In file included from /home/esc/anaconda/lib/python2.7/site-packages/numpy/core/include/numpy/ndarraytypes.h:1722, from /home/esc/anaconda/lib/python2.7/site-packages/numpy/core/include/numpy/ndarrayobject.h:17, from /home/esc/anaconda/lib/python2.7/site-packages/numpy/core/include/numpy/arrayobject.h:15, from cos_doubles_wrap.c:2706: /home/esc/anaconda/lib/python2.7/site-packages/numpy/core/include/numpy/npy_deprecated_api.h:11:2: warning: #warning "Using deprecated NumPy API, disable it by #defining NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION" gcc -pthread -shared build/temp.linux-x86_64-2.7/cos_doubles.o build/temp.linux-x86_64-2.7/cos_doubles_wrap.o -L/home/esc/anaconda/lib -lpython2.7 -o /home/esc/git-working/scipy-lecture-notes/advanced/interfacing_with_c/swig_numpy/_cos_doubles.so $ ls build/ cos_doubles.h cos_doubles.py cos_doubles_wrap.c setup.py cos_doubles.c cos_doubles.i _cos_doubles.so* numpy.i test_cos_doubles.py ``` 并且,和前面一樣,我們來驗證一下它工作正常: In?[?]: ``` import numpy as np import pylab import cos_doubles x = np.arange(0, 2 * np.pi, 0.1) y = np.empty_like(x) cos_doubles.cos_doubles_func(x, y) pylab.plot(x, y) pylab.show() ``` ![](http://www.scipy-lectures.org/_images/test_cos_doubles1.png) ## 2.8.5 Cython Cython既是寫C擴展的類Python語言,也是這種語言的編譯器。Cython語言是Python的超集,帶有額外的結構,允許你調用C函數和C類型的注釋變量和類屬性。在這個意義上,可以稱之為帶有類型的Python。 除了封裝原生代碼的基礎應用案例,Cython也支持額外的應用案例,即交互優化。從根本上來說,從純Python腳本開始,向瓶頸代碼逐漸增加Cython類型來優化那些確實有影響的代碼。 在這種情況下,與SWIG很相似,因為代碼可以自動生成,但是,從另一個角度來說,又與ctypes很類似,因為,封裝代碼(大部分)是用Python寫的。 盡管其他自動生成代碼的解決方案很難debug(比如SWIG),Cython有一個GNU debugger擴展來幫助debug Python,Cython和C代碼。 **注意** 自動生成的C代碼使用Python-C-Api。 **優點** * 類Python語言來寫擴展 * 自動生成代碼 * 支持增量優化 * 包含一個GNU debugger擴展 * 支持C++ (從版本0.13) **不足** * 必須編譯 * 需要額外的庫 ( 只是在build的時候, 在這個問題中,可以通過運送生成的C文件來克服) ### 2.8.5.1 例子 `cos_module`的主要Cython代碼包含在文件`cos_module.pyx`中: In?[?]: ``` """ Example of wrapping cos function from math.h using Cython. """ cdef extern from "math.h": double cos(double arg) def cos_func(arg): return cos(arg) ``` 注意額外的關鍵詞,比如`cdef`和`extern`。同時,`cos_func`也是純Python。 和前面一樣,我們可以使用標準的`distutils`模塊,但是,這次我們需要一些來自于`Cython.Distutils`的更多代碼: In?[?]: ``` from distutils.core import setup, Extension from Cython.Distutils import build_ext setup( cmdclass={'build_ext': build_ext}, ext_modules=[Extension("cos_module", ["cos_module.pyx"])] ) ``` 編譯這個模塊: In?[?]: ``` $ cd advanced/interfacing_with_c/cython $ ls cos_module.pyx setup.py $ python setup.py build_ext --inplace running build_ext cythoning cos_module.pyx to cos_module.c building 'cos_module' extension creating build creating build/temp.linux-x86_64-2.7 gcc -pthread -fno-strict-aliasing -g -O2 -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -fPIC -I/home/esc/anaconda/include/python2.7 -c cos_module.c -o build/temp.linux-x86_64-2.7/cos_module.o gcc -pthread -shared build/temp.linux-x86_64-2.7/cos_module.o -L/home/esc/anaconda/lib -lpython2.7 -o /home/esc/git-working/scipy-lecture-notes/advanced/interfacing_with_c/cython/cos_module.so $ ls build/ cos_module.c cos_module.pyx cos_module.so* setup.py ``` 并且運行: In?[?]: ``` In [1]: import cos_module In [2]: cos_module? Type: module String Form:<module 'cos_module' from 'cos_module.so'> File: /home/esc/git-working/scipy-lecture-notes/advanced/interfacing_with_c/cython/cos_module.so Docstring: <no docstring> In [3]: dir(cos_module) Out[3]: ['__builtins__', '__doc__', '__file__', '__name__', '__package__', '__test__', 'cos_func'] In [4]: cos_module.cos_func(1.0) Out[4]: 0.5403023058681398 In [5]: cos_module.cos_func(0.0) Out[5]: 1.0 In [6]: cos_module.cos_func(3.14159265359) Out[6]: -1.0 ``` 并且,測試一下強壯性,我們可以看到我們得到了更好的錯誤信息: In?[?]: ``` In [7]: cos_module.cos_func('foo') --------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-7-11bee483665d> in <module>() ----> 1 cos_module.cos_func('foo') /home/esc/git-working/scipy-lecture-notes/advanced/interfacing_with_c/cython/cos_module.so in cos_module.cos_func (cos_module.c:506)() TypeError: a float is required ``` 此外,不需要Cython完全傳輸到C math庫的聲明,上面的代碼可以簡化為: In?[?]: ``` """ Simpler example of wrapping cos function from math.h using Cython. """ from libc.math cimport cos def cos_func(arg): return cos(arg) ``` 在這種情況下,`cimport`語句用于導入`cos`函數。 ### 2.8.5.2 Numpy支持 Cython通過`numpy.pyx`文件支持Numpy,允許你為你的Cython代碼添加Numpy數組類型,即就像指定變量`i`是`int`類型,你也可以指定變量`a`是帶有給定的`dtype`的`numpy.ndarray`。同時,同時特定的優化比如邊際檢查也是支持的。看一下[Cython文檔](http://docs.cython.org/src/tutorial/numpy.html)的對應部分。如果你想要將Numpy數組作為C數組傳遞給Cython封裝的C函數,在[Cython wiki](http://wiki.cython.org/tutorials/NumpyPointerToC)上有對應的部分。 在下面的例子中,我們將演示如何用Cython來封裝類似的`cos_doubles`。 In?[?]: ``` void cos_doubles(double * in_array, double * out_array, int size); ``` In?[?]: ``` #include <math.h> /* Compute the cosine of each element in in_array, storing the result in * out_array. */ void cos_doubles(double * in_array, double * out_array, int size){ int i; for(i=0;i<size;i++){ out_array[i] = cos(in_array[i]); } } ``` 這個函數使用下面的Cython代碼來封裝`cos_doubles_func`: In?[?]: ``` """ Example of wrapping a C function that takes C double arrays as input using the Numpy declarations from Cython """ # cimport the Cython declarations for numpy cimport numpy as np # if you want to use the Numpy-C-API from Cython # (not strictly necessary for this example, but good practice) np.import_array() # cdefine the signature of our c function cdef extern from "cos_doubles.h": void cos_doubles (double * in_array, double * out_array, int size) # create the wrapper code, with numpy type annotations def cos_doubles_func(np.ndarray[double, ndim=1, mode="c"] in_array not None, np.ndarray[double, ndim=1, mode="c"] out_array not None): cos_doubles(<double*> np.PyArray_DATA(in_array), <double*> np.PyArray_DATA(out_array), in_array.shape[0]) ``` 可以使用`distutils`來編譯: In?[?]: ``` from distutils.core import setup, Extension import numpy from Cython.Distutils import build_ext setup( cmdclass={'build_ext': build_ext}, ext_modules=[Extension("cos_doubles", sources=["_cos_doubles.pyx", "cos_doubles.c"], include_dirs=[numpy.get_include()])], ) ``` 與前面的編譯Numpy例子類似,我們需要`include_dirs`選項。 In?[?]: ``` $ ls cos_doubles.c cos_doubles.h _cos_doubles.pyx setup.py test_cos_doubles.py $ python setup.py build_ext -i running build_ext cythoning _cos_doubles.pyx to _cos_doubles.c building 'cos_doubles' extension creating build creating build/temp.linux-x86_64-2.7 gcc -pthread -fno-strict-aliasing -g -O2 -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -fPIC -I/home/esc/anaconda/lib/python2.7/site-packages/numpy/core/include -I/home/esc/anaconda/include/python2.7 -c _cos_doubles.c -o build/temp.linux-x86_64-2.7/_cos_doubles.o In file included from /home/esc/anaconda/lib/python2.7/site-packages/numpy/core/include/numpy/ndarraytypes.h:1722, from /home/esc/anaconda/lib/python2.7/site-packages/numpy/core/include/numpy/ndarrayobject.h:17, from /home/esc/anaconda/lib/python2.7/site-packages/numpy/core/include/numpy/arrayobject.h:15, from _cos_doubles.c:253: /home/esc/anaconda/lib/python2.7/site-packages/numpy/core/include/numpy/npy_deprecated_api.h:11:2: warning: #warning "Using deprecated NumPy API, disable it by #defining NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION" /home/esc/anaconda/lib/python2.7/site-packages/numpy/core/include/numpy/__ufunc_api.h:236: warning: ‘_import_umath’ defined but not used gcc -pthread -fno-strict-aliasing -g -O2 -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -fPIC -I/home/esc/anaconda/lib/python2.7/site-packages/numpy/core/include -I/home/esc/anaconda/include/python2.7 -c cos_doubles.c -o build/temp.linux-x86_64-2.7/cos_doubles.o gcc -pthread -shared build/temp.linux-x86_64-2.7/_cos_doubles.o build/temp.linux-x86_64-2.7/cos_doubles.o -L/home/esc/anaconda/lib -lpython2.7 -o /home/esc/git-working/scipy-lecture-notes/advanced/interfacing_with_c/cython_numpy/cos_doubles.so $ ls build/ _cos_doubles.c cos_doubles.c cos_doubles.h _cos_doubles.pyx cos_doubles.so* setup.py test_cos_doubles.py ``` 和前面一樣,我們來驗證一下它是有效的: In?[?]: ``` import numpy as np import pylab import cos_doubles x = np.arange(0, 2 * np.pi, 0.1) y = np.empty_like(x) cos_doubles.cos_doubles_func(x, y) pylab.plot(x, y) pylab.show() ``` ![](http://www.scipy-lectures.org/_images/test_cos_doubles2.png) ### 2.8.6 總結 這個部分演示了四種與原生代碼交互的技術。下表概述了這些技術的一些方面。 | x | Part of CPython | Compiled | Autogenerated | Numpy Support | | --- | --- | --- | --- | --- | | Python-C-API | True | True | False | True | | Ctypes | True | False | False | True | | Swig | False | True | True | True | | Cython | False | True | True | True | 在上面的技術中,Cython是最現代最高級的。特別是,通過為Python代碼添加類型來增量優化代碼的技術是惟一的。 ## 2.8.7 Further Reading and References [Ga?l Varoquaux關于避免數據復制的博客](http://gael-varoquaux.info/blog/?p=157)給出了一些如何精明的處理內存管理的見解。如果你在大數據量時出現問題,可以回到這里尋找一些靈感。 ## 2.8.8 練習 因為這是一個新部分,練習更像是一個接下來應該查看什么的指示器,因此,看一下那些你覺得更有趣的部分。如果你有關于練習更好點子,請告訴我們! * 下載每個例子的源碼,并且在你的機器上運行并編譯他們。 * 對每個例子做一些修改,并且自己驗證一下是否有效。 ( 比如,將cos改為sin。) * 絕大多數例子,特別是包含了Numpy的例子,可能還是比較脆弱,對輸入錯誤反應較差。找一些方法來讓例子崩潰,找出問題所在,并且設計潛在的解決方案。這些是有些點子: * 數字溢出 * 輸入輸出數組長度不一致 * 多維度數據 * 空數組 * non-double類型數組 * 使用`%timeit`IPython魔法函數來測量不同解決方案的執行時間 ### 2.8.8.1 Python-C-API * 修改Numpy例子以便函數有兩個輸入參數,第二個參數是預分配輸出數組,讓它與其他的Numpy例子一致。 * 修改這個例子,以便這個函數只有一個輸入數組,在原地修改這個函數。 * 試著用新的[Numpy迭代協議](http://docs.scipy.org/doc/numpy/reference/c-api.iterator.html)修改例子。如果你剛好獲得了一個可用的解決方案,請將其提交一個請求到github。 * 你可能注意到了,Numpy-C-API例子只是Numpy例子沒有封裝`cos_doubles`但是直接將`cos`函數應用于Numpy數組的元素上。這樣做與其他技術相比有什么優勢。 * 你可以只用Numpy-C-API來封裝`cos_doubles`。你可能需要確保數組有正確的類型,并且是單維度和內存臨近。 ### 2.8.8.2 Ctypes * 修改Numpy例子以便`cos_doubles_func`為你處理預分配,讓它更像Numpy-C-API例子。 ### 2.8.8.3\. SWIG * 看一下SWIG自動生成的代碼,你能理解多少? * 修改Numpy例子,以便`cos_doubles_func`為你處理預處理,讓它更像Numpy-C-API例子。 * 修改`cos_doubles` C 函數,以便它返回分配的數組。你可以用SWIG typemaps類封裝嗎? 如果不可以,為什么不可以? 對于這種特殊的情況有沒有什么變通方法? (提示: 你知道輸出數組的大小, 因此,可以從返回的`double \*`構建Numpy數組。 ### 2.8.8.4 Cython * 看一下Cython自動生成的代碼。仔細看一下Cython插入的一些評論。你能看到些什么? * 看一下Cython文檔中[與Numpy工作](http://docs.cython.org/src/tutorial/numpy.html)的部分, 學習一下如何使用Numpy增量優化python腳本。 * 修改Numpy例子,以便`cos_doubles_func`為你處理預處理,讓它更像Numpy-C-API例子。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看