如何用C代码给Python写扩展库(Cython)


Posted in Python onMay 17, 2019

之前一篇文章里提到了利用Cython来编译Python,这次来讲一下如何用Cython给Python写扩展库。

两种语言混合编程,其中最重要的是类型的传递。

我们用一个简单的例子进行入门:这次的目标是用C语言写一个Numpy的加法和元素相乘模块。在本例中,Numpy的array被传入到C语言模块内,变成了二维数组。

1. 头文件main.h:

#ifndef _MAIN_H
#define _MAIN_H
void plus(double *a, double *b, double *r, int n, int m); // 矩阵加法
void mul(double *a, double *b, double *r, int n, int m); // 矩阵按元素相乘
void main(double *a, double *b, double *r, int n, int m, int times); // 用于测试的main函数
#endif

2.  把主要代码写在main.c中:

#include "main.h"
 
/***********************************
* 矩阵的加法
* 利用数组是顺序存储的特性, *
* 通过降维来访问二维数组! *
* r
***********************************/
void plus(double *a, double *b, double *r, int n, int m)
{
  int i, j;
  for(i = 0; i < n; i++)
  {
    for(j = 0; j < m; j++)
      *(r + i*m + j) = *(a + i*m + j) + *(b + i*m + j);
  }
}
 
/***********************************
* 矩阵的按元素乘法
* 利用数组是顺序存储的特性, *
* 通过降维来访问二维数组! *
* r
***********************************/
void mul(double *a, double *b, double *r, int n, int m)
{
  int i, j;
  for(i = 0; i < n; i++)
  {
    for(j = 0; j < m; j++)
      *(r + i*m + j) = *(a + i*m + j) * *(b + i*m + j);
  }
}
 
/***********************************
* main函数
* 利用数组是顺序存储的特性, *
* 通过降维来访问二维数组! *
* r
***********************************/
void main(double *a, double *b, double *r, int n, int m, int times)
{
  int i;
  // 循环times次
#pragma omp parallel for
  for (i = 0; i < times; i++)
  {
    // 矩阵的加法
    plus(a, b, r, n, m);
    
    // 矩阵按元素相乘
    mul(a, b, r, n, m);
  }
}

这个main.c中实现了矩阵的加法、矩阵按元素相乘的功能,用到的数据结构是二维数组,但是因为C语言中给函数传递二维数组比较麻烦,这里用降维的方法实现。另外在main()函数中,采用一个循环来进行测试,以测试性能。

3. 下面编写test.pyx文件来调用上述C函数(注意,后缀是.pyx噢):详细的知识点在注释中写出来了~

# 既要import numpy, 也要用cimport numpy
import time
import numpy as np
cimport numpy as np
 
# 使用Numpy-C-API
np.import_array()
 
# cdefine C 函数
cdef extern from "main.h":
  void plus(double *a, double *b, double *r, int n, int m)
  void mul(double *a, double *b, double *r, int n, int m)
  void main(double *a, double *b, double *r, int n, int m, int times)
 
"""
# 定义一个"包装函数", 用于调用C语言的main函数,调用范例:plus_fun(a, b, r)
# 在这里要注意函数传入的参数的类型声明,double表示数组的元素是double类型的,
# ndim = 2表示数组的维度是2
# 在调用main函数时,要把python的变量强制转化成相应的类型(以确保无误),比如<int>
# 当然,基本类型如int,可以不显式地写出来,如下面的a.shape[0]、a.shape[1]
"""
def main_func(np.ndarray[double, ndim=2, mode="c"] a not None,
           np.ndarray[double, ndim=2, mode="c"] b not None, 
           np.ndarray[double, ndim=2, mode="c"] r not None,
           times not None):
  main(<double*> np.PyArray_DATA(a),
        <double*> np.PyArray_DATA(b),
        <double*> np.PyArray_DATA(r),
        a.shape[0],
        a.shape[1],
        <int> times)

4. 为了用Cython编译上述代码,我们创建一个setup.py文件:

import numpy
from distutils.core import setup
from distutils.extension import Extension
from Cython.Distutils import build_ext
 
filename = 'test' # 源文件名
full_filename = 'test.pyx' # 包含后缀的源文件名
 
setup(
  name = 'test',
  cmdclass = {'build_ext': build_ext},
  ext_modules=[Extension(filename,sources=[full_filename, "main.c"],
         include_dirs=[numpy.get_include()])],
)

5. 上述的main.h、main.c、test.pyx一定要放在同一个文件夹下。此时在该文件夹下按住shift键,然后右击鼠标,打开cmd或PowerShell控制台,在控制台中运行以下命令进行Cython编译:

python setup.py build_ext --i

或者:

python setup.py build_ext --inplace

编译成功的图例:

如何用C代码给Python写扩展库(Cython)

此时在同目录下会生成“test.cp36-win_amd64.pyd”的二进制码文件,它是闭源的,但是可以直接用python来import。下面编写测试代码main.py来进行测试:

import test
import time
import numpy as np
 
start_time = time.time()
a = np.random.rand(100, 100) * 2 - 1 # 生成300*300的随即矩阵
b = np.random.rand(100, 100) * 2 - 1
r = np.empty_like(a) # 创建一个空矩阵,用来存储计算结果
test.main_func(a, b, r, 500000) # 调用main_func进行测试
end_time = time.time()
print(end_time - start_time) # 输出时间
print(r) # 输出运行结果

执行结果:

如何用C代码给Python写扩展库(Cython)

通过本例我们可以看到:将循环放在C语言模块中,而不是原生的Python中,可以提高执行效率。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
详解在Python和IPython中使用Docker
Apr 28 Python
python轻松查到删除自己的微信好友
Jan 10 Python
python 默认参数问题的陷阱
Feb 29 Python
关于反爬虫的一些简单总结
Dec 13 Python
答题辅助python代码实现
Jan 16 Python
Anaconda下安装mysql-python的包实例
Jun 11 Python
Centos下实现安装Python3.6和Python2共存
Aug 15 Python
python pandas消除空值和空格以及 Nan数据替换方法
Oct 30 Python
pycharm修改界面主题颜色的方法
Jan 17 Python
Django用户认证系统 Web请求中的认证解析
Aug 02 Python
Python实现中值滤波去噪方式
Dec 18 Python
Python 2.6.6升级到Python2.7.15的详细步骤
Dec 14 Python
python实现坦克大战游戏 附详细注释
Mar 27 #Python
六行python代码的爱心曲线详解
May 17 #Python
python使用pygame模块实现坦克大战游戏
Mar 25 #Python
Django如何开发简单的查询接口详解
May 17 #Python
详解python函数的闭包问题(内部函数与外部函数详述)
May 17 #Python
学习python分支结构
May 17 #Python
python pygame实现方向键控制小球
May 17 #Python
You might like
function.inc.php超越php
2006/12/09 PHP
腾讯QQ微博API接口获取微博内容
2013/10/30 PHP
PHP扩展Memcache分布式部署方案
2015/12/06 PHP
PHP钩子与简单分发方式实例分析
2017/09/04 PHP
JavaScript Event学习第九章 鼠标事件
2010/02/08 Javascript
基于JQuery实现异步刷新的代码(转载)
2011/03/29 Javascript
JQuery入门——用one()方法绑定事件处理函数(仅触发一次)
2013/02/05 Javascript
jquery在Chrome下获取图片的长宽问题解决
2013/03/20 Javascript
JS 屏蔽按键效果与改变按键效果的示例代码
2013/12/24 Javascript
关于页面嵌入swf覆盖div层的问题的解决方法
2014/02/11 Javascript
js+div实现文字滚动和图片切换效果代码
2015/08/27 Javascript
JS表格组件BootstrapTable行内编辑解决方案x-editable
2016/09/01 Javascript
详解JS中定时器setInterval和setTImeout的this指向问题
2017/01/06 Javascript
Vue2单一事件管理组件通信
2017/05/09 Javascript
nodeJS实现路由功能实例代码
2017/06/08 NodeJs
基于JS实现一个随机生成验证码功能
2019/05/29 Javascript
深入了解JavaScript 的 WebAssembly
2019/06/15 Javascript
socket + select 完成伪并发操作的实例
2017/08/15 Python
python实现手机通讯录搜索功能
2018/02/22 Python
python自动重试第三方包retrying模块的方法
2018/04/24 Python
Python使用min、max函数查找二维数据矩阵中最小、最大值的方法
2018/05/15 Python
python 字典中取值的两种方法小结
2018/08/02 Python
解决Python pandas plot输出图形中显示中文乱码问题
2018/12/12 Python
python使用suds调用webservice接口的方法
2019/01/03 Python
python实现将json多行数据传入到mysql中使用
2019/12/31 Python
Python 私有属性和私有方法应用场景分析
2020/06/19 Python
pandas.DataFrame.drop_duplicates 用法介绍
2020/07/06 Python
matplotlib基础绘图命令之errorbar的使用
2020/08/13 Python
python 模拟登陆github的示例
2020/12/04 Python
CSS3 圆角效果
2009/07/15 HTML / CSS
使用纯HTML5编写一款网页上的时钟的代码分享
2015/11/16 HTML / CSS
boostrap modal 闪现问题的解决方法
2020/09/01 HTML / CSS
2014统计局民主生活会对照检查材料思想汇报
2014/10/02 职场文书
2015年元宵节活动总结
2015/02/06 职场文书
Python移位密码、仿射变换解密实例代码
2021/06/27 Python
总结python多进程multiprocessing的相关知识
2021/06/29 Python