Files
ailearning/docs/da/093.md
2020-10-19 21:08:55 +08:00

353 lines
5.9 KiB
Markdown
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Python 扩展模块
## 简介
| C Library | Interface | Python |
| --- | --- | --- |
| `c header`
`c implementation` | Wrapper `C` $\leftrightarrows$ `Python`
communication between `py + c` | `import fact`
`fact.fact(10)` |
**Python** 扩展模块将 `PyInt(10)` 转化为 `CInt(10)` 然后调用 `C` 程序中的 `fact()` 函数进行计算,再将返回的结果转换回 `PyInt`
## 产生一个扩展模块
假设我们有这样的一个头文件和程序:
In [1]:
```py
%%file fact.h
#ifndef FACT_H
#define FACT_h
int fact(int n);
#endif
```
```py
Writing fact.h
```
In [2]:
```py
%%file fact.c
#include "fact.h"
int fact(int n)
{
if (n <= 1) return 1;
else return n * fact(n - 1);
}
```
```py
Writing fact.c
```
定义包装函数:
In [3]:
```py
%%file fact_wrap.c
/* Must include Python.h before any standard headers*/
#include <Python.h>
#include "fact.h"
static PyObject* wrap_fact(PyObject *self, PyObject *args)
{
/* Python->C data conversion */
int n, result;
// the string i here means there is only one integer
if (!PyArg_ParseTuple(args, "i", &n))
return NULL;
/* C Function Call */
result = fact(n);
/* C->Python data conversion */
return Py_BuildValue("i", result);
}
/* Method table declaring the names of functions exposed to Python*/
static PyMethodDef ExampleMethods[] = {
{"fact", wrap_fact, METH_VARARGS, "Calculate the factorial of n"},
{NULL, NULL, 0, NULL} /* Sentinel */
};
/* Module initialization function called at "import example"*/
PyMODINIT_FUNC
initexample(void)
{
(void) Py_InitModule("example", ExampleMethods);
}
```
```py
Writing fact_wrap.c
```
## 手动编译扩展模块
手动使用 `gcc` 编译,`Windows` 下如果没有 `gcc`,可以通过 `conda` 进行安装:
```py
conda install mingw4
```
`Window 64-bit` 下编译需要加上 `-DMS_WIN64` 的选项,`include``lib` 文件夹的路径对应于本地 **Python** 安装的环境:
In [4]:
```py
!gcc -DMS_WIN64 -c fact.c fact_wrap.c -IC:\Miniconda\include
```
In [5]:
```py
!gcc -DMS_WIN64 -shared fact.o fact_wrap.o -LC:\Miniconda\libs -lpython27 -o example.pyd
```
`Windows` 下最终生成的文件后缀为 `.pyd` `Unix` 下生成的文件后缀名为 `.so`
用法为:
* `Windows 32-bit`
```py
gcc -c fact.c fact_wrap.c -I<your python path>\include
gcc -shared fact.o fact_wrap.o -L<your python path>\libs -lpython27 -o example.pyd
```
* `Unix`
```py
gcc -c fact.c fact_wrap.c -I<your python path>
gcc -shared fact.o fact_wrap.o -L<your python path>\config -lpython27 -o example.so
```
编译完成后,我们就可以使用 `example` 这个模块了。
导入生成的包:
In [6]:
```py
import example
print dir(example)
```
```py
['__doc__', '__file__', '__name__', '__package__', 'fact']
```
使用 `example` 中的函数:
In [7]:
```py
print 'factorial of 10:', example.fact(10)
```
```py
factorial of 10: 3628800
```
## 使用 setup.py 进行编译
清理刚才生成的文件:
In [8]:
```py
!rm -f example.pyd
```
写入 `setup.py`
In [9]:
```py
%%file setup.py
from distutils.core import setup, Extension
ext = Extension(name='example', sources=['fact_wrap.c', 'fact.c'])
setup(name='example', ext_modules=[ext])
```
```py
Writing setup.py
```
使用 `distutils` 中的函数,我们进行 `build` 和 `install`
```py
python setup.py build (--compiler=mingw64)
python setup.py install
```
括号中的内容在 `windows` 中可能需要加上。
这里我们使用 `build_ext --inplace` 选项将其安装在本地文件夹:
In [10]:
```py
!python setup.py build_ext --inplace
```
```py
running build_ext
building 'example' extension
creating build
creating build\temp.win-amd64-2.7
creating build\temp.win-amd64-2.7\Release
C:\Miniconda\Scripts\gcc.bat -DMS_WIN64 -mdll -O -Wall -IC:\Miniconda\include -IC:\Miniconda\PC -c fact_wrap.c -o build\temp.win-amd64-2.7\Release\fact_wrap.o
C:\Miniconda\Scripts\gcc.bat -DMS_WIN64 -mdll -O -Wall -IC:\Miniconda\include -IC:\Miniconda\PC -c fact.c -o build\temp.win-amd64-2.7\Release\fact.o
writing build\temp.win-amd64-2.7\Release\example.def
C:\Miniconda\Scripts\gcc.bat -DMS_WIN64 -shared -s build\temp.win-amd64-2.7\Release\fact_wrap.o build\temp.win-amd64-2.7\Release\fact.o build\temp.win-amd64-2.7\Release\example.def -LC:\Miniconda\libs -LC:\Miniconda\PCbuild\amd64 -lpython27 -lmsvcr90 -o "C:\Users\Jin\Documents\Git\python-tutorial\07\. interfacing with other languages\example.pyd"
```
## 使用编译的模块
进行测试:
In [11]:
```py
import example
print 'factorial of 10:', example.fact(10)
```
```py
factorial of 10: 3628800
```
定义 `Python` 函数:
In [12]:
```py
def pyfact(n):
if n <= 1: return 1
return n * pyfact(n-1)
print pyfact(10)
print example.fact(10)
```
```py
3628800
3628800
```
时间测试:
In [13]:
```py
%timeit example.fact(10)
```
```py
The slowest run took 13.17 times longer than the fastest. This could mean that an intermediate result is being cached
1000000 loops, best of 3: 213 ns per loop
```
In [14]:
```py
%timeit pyfact(10)
```
```py
1000000 loops, best of 3: 1.43 µs per loop
```
如果使用 `fact` 计算比较大的值:
In [15]:
```py
example.fact(100)
```
Out[15]:
```py
0
```
会出现溢出的结果,因为 `int` 表示的值有限,但是 `pyfact` 不会有这样的问题:
In [16]:
```py
pyfact(100)
```
Out[16]:
```py
93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000L
```
将生成的文件压缩到压缩文件中:
In [17]:
```py
import zipfile
f = zipfile.ZipFile('07-02-example.zip','w',zipfile.ZIP_DEFLATED)
names = 'fact.o fact_wrap.c fact_wrap.o example.pyd setup.py'.split()
for name in names:
f.write(name)
f.close()
```
清理生成的文件:
In [18]:
```py
!rm -f fact*.*
!rm -f example.*
!rm -f setup*.*
!rm -rf build
```