如何用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格式化压缩后的JS文件的方法
Mar 05 Python
Python多线程编程(二):启动线程的两种方法
Apr 05 Python
python 实现一个贴吧图片爬虫的示例
Oct 12 Python
python 通过 socket 发送文件的实例代码
Aug 14 Python
Python3 导入上级目录中的模块实例
Feb 16 Python
Python基础之循环语句用法示例【for、while循环】
Mar 23 Python
Python3.5面向对象编程图文与实例详解
Apr 24 Python
python点击鼠标获取坐标(Graphics)
Aug 10 Python
解决python多行注释引发缩进错误的问题
Aug 23 Python
opencv-python 读取图像并转换颜色空间实例
Dec 09 Python
Python 将 QQ 好友头像生成祝福语的实现代码
May 03 Python
Python局部变量与全局变量区别原理解析
Jul 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
人族 Terran 基本策略
2020/03/14 星际争霸
php实现扫描二维码根据浏览器类型访问不同下载地址
2014/10/15 PHP
mac下多个php版本快速切换的方法
2016/10/09 PHP
PHP自定义多进制的方法
2016/11/03 PHP
利用php-cli和任务计划实现刷新token功能的方法
2017/05/03 PHP
解决php-fpm.service not found问题的办法
2017/06/06 PHP
js left,right,mid函数
2008/06/10 Javascript
jquery选择器之层级过滤选择器详解
2014/01/27 Javascript
javascript闭包传参和事件的循环绑定示例探讨
2014/04/17 Javascript
jQuery制作简单柱状图实例
2015/01/28 Javascript
jquery调整表格行tr上下顺序实例讲解
2016/01/09 Javascript
使用JavaScript实现ajax的实例代码
2016/05/11 Javascript
jQuery+CSS实现简单切换菜单示例
2016/07/27 Javascript
AngularJS入门教程之表格实例详解
2016/07/27 Javascript
JavaScript实现单击网页任意位置打开新窗口与关闭窗口的方法
2017/09/21 Javascript
echarts鼠标覆盖高亮显示节点及关系名称详解
2018/03/17 Javascript
基于vue中keep-alive缓存问题的解决方法
2018/09/21 Javascript
JS实现将对象转化为数组的方法分析
2019/01/21 Javascript
环形加载进度条封装(Vue插件版和原生js版)
2019/12/04 Javascript
跟老齐学Python之有点简约的元组
2014/09/24 Python
Python实现视频下载功能
2017/03/14 Python
Python零基础入门学习之输入与输出
2019/04/03 Python
Django REST framework 分页的实现代码
2019/06/19 Python
pyqt5实现绘制ui,列表窗口,滚动窗口显示图片的方法
2019/06/20 Python
python集合删除多种方法详解
2020/02/10 Python
python--shutil移动文件到另一个路径的操作
2020/07/13 Python
PyQt5多线程防卡死和多窗口用法的实现
2020/09/15 Python
CSS3 Flexbox中flex-shrink属性的用法示例介绍
2013/12/30 HTML / CSS
赫里福德的一家乡村零售商店:Philip Morris & Son
2017/06/25 全球购物
shallow copy和deep copy的区别
2016/05/09 面试题
药剂专业学生求职信范文
2013/12/28 职场文书
英语生日邀请函
2014/01/23 职场文书
通用自荐信范文
2014/03/14 职场文书
数控技校生自我鉴定
2014/04/19 职场文书
党的群众路线教育实践活动个人整改措施范文
2014/11/04 职场文书
被告代理词范文
2015/05/25 职场文书