返回介绍

01. Python 工具

02. Python 基础

03. Numpy

04. Scipy

05. Python 进阶

06. Matplotlib

07. 使用其他语言进行扩展

08. 面向对象编程

09. Theano 基础

10. 有趣的第三方模块

11. 有用的工具

12. Pandas

Python 扩展模块

发布于 2022-09-03 20:46:14 字数 9855 浏览 0 评论 0 收藏 0

简介

C LibraryInterfacePython
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]:

%%file fact.h
#ifndef FACT_H
#define FACT_h
int fact(int n);
#endif
Writing fact.h

In [2]:

%%file fact.c
#include "fact.h"
int fact(int n)
{
    if (n <= 1) return 1;
    else return n * fact(n - 1);
}
Writing fact.c

定义包装函数:

In [3]:

%%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);
}
Writing fact_wrap.c

手动编译扩展模块

手动使用 gcc 编译,Windows 下如果没有 gcc,可以通过 conda 进行安装:

conda install mingw4

Window 64-bit 下编译需要加上 -DMS_WIN64 的选项,includelib 文件夹的路径对应于本地 Python 安装的环境:

In [4]:

!gcc -DMS_WIN64 -c fact.c fact_wrap.c -IC:\Miniconda\include

In [5]:

!gcc -DMS_WIN64 -shared fact.o fact_wrap.o -LC:\Miniconda\libs -lpython27 -o example.pyd

Windows 下最终生成的文件后缀为 .pydUnix 下生成的文件后缀名为 .so

用法为:

  • Windows 32-bit

    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

    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]:

import example
print dir(example)
['__doc__', '__file__', '__name__', '__package__', 'fact']

使用 example 中的函数:

In [7]:

print 'factorial of 10:', example.fact(10)
factorial of 10: 3628800

使用 setup.py 进行编译

清理刚才生成的文件:

In [8]:

!rm -f example.pyd

写入 setup.py

In [9]:

%%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])
Writing setup.py

使用 distutils 中的函数,我们进行 buildinstall

python setup.py build (--compiler=mingw64)
python setup.py install

括号中的内容在 windows 中可能需要加上。

这里我们使用 build_ext --inplace 选项将其安装在本地文件夹:

In [10]:

!python setup.py build_ext --inplace
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]:

import example

print 'factorial of 10:', example.fact(10)
factorial of 10: 3628800

定义 Python 函数:

In [12]:

def pyfact(n):
    if n <= 1: return 1
    return n * pyfact(n-1)

print pyfact(10)
print example.fact(10)
3628800
3628800

时间测试:

In [13]:

%timeit example.fact(10)
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]:

%timeit pyfact(10)
1000000 loops, best of 3: 1.43 µs per loop

如果使用 fact 计算比较大的值:

In [15]:

example.fact(100)

Out[15]:

0

会出现溢出的结果,因为 int 表示的值有限,但是 pyfact 不会有这样的问题:

In [16]:

pyfact(100)

Out[16]:

93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000L

将生成的文件压缩到压缩文件中:

In [17]:

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]:

!rm -f fact*.*
!rm -f example.*
!rm -f setup*.*
!rm -rf build

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文