如何用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的*args和**kwargs用法
Nov 01 Python
Python入门篇之正则表达式
Oct 20 Python
python判断字符串是否纯数字的方法
Nov 19 Python
Python中用startswith()函数判断字符串开头的教程
Apr 07 Python
Python通过future处理并发问题
Oct 17 Python
Django 通过JS实现ajax过程详解
Jul 30 Python
jenkins配置python脚本定时任务过程图解
Oct 29 Python
python随机生成库faker库api实例详解
Nov 28 Python
搭建pypi私有仓库实现过程详解
Nov 25 Python
python lambda的使用详解
Feb 26 Python
字典算法实现及操作 --python(实用)
Mar 31 Python
PyTorch device与cuda.device用法
Apr 03 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
破解.net程序(dll文件)编译和反编译方法
2013/01/31 PHP
php实现加减法验证码代码
2014/02/14 PHP
php5.3以后的版本连接sqlserver2000的方法
2014/07/28 PHP
PHP基础之输出缓冲区基本概念、原理分析
2019/06/19 PHP
js和as的稳定传值问题解决
2013/07/14 Javascript
Javascript中判断变量是数组还是对象(array还是object)
2013/08/14 Javascript
jquery UI Datepicker时间控件的使用方法(基础版)
2015/11/07 Javascript
详解JavaScript的AngularJS框架中的作用域与数据绑定
2016/03/04 Javascript
js中数组的常用方法小结
2016/12/30 Javascript
JS排序算法之希尔排序与快速排序实现方法
2017/12/12 Javascript
vue二级菜单导航点击选中事件的方法
2018/09/12 Javascript
vue.js父子组件通信动态绑定的实例
2018/09/28 Javascript
Vue.js中对css的操作(修改)具体方式详解
2018/10/30 Javascript
Javascript数组方法reduce的妙用之处分享
2019/06/10 Javascript
小程序中英文混合排序问题解决
2019/08/02 Javascript
Vue 使用Props属性实现父子组件的动态传值详解
2019/11/13 Javascript
前端vue如何使用高德地图
2020/11/05 Javascript
Python深入学习之上下文管理器
2014/08/31 Python
Python实现简单HTML表格解析的方法
2015/06/15 Python
python类和继承用法实例
2015/07/07 Python
Django权限机制实现代码详解
2018/02/05 Python
解决python3 网络请求路径包含中文的问题
2018/05/10 Python
pygame游戏之旅 python和pygame安装教程
2018/11/20 Python
python中常用的数据结构介绍
2021/01/12 Python
如何写出高性能的JSP和Servlet
2013/01/22 面试题
医药工作岗位求职信分享
2013/12/31 职场文书
产品发布会策划方案
2014/05/12 职场文书
中文专业毕业生自荐信
2014/05/24 职场文书
网上祭先烈心得体会
2014/09/01 职场文书
村委会贫困证明范文
2014/09/21 职场文书
党的群众路线教育实践活动总结
2014/10/30 职场文书
毕业生就业推荐表导师评语
2014/12/31 职场文书
通用员工手册范本
2015/05/14 职场文书
2016国培研修心得体会
2016/01/08 职场文书
MySQL系列之十 MySQL事务隔离实现并发控制
2021/07/02 MySQL
Python socket如何解析HTTP请求内容
2022/02/12 Python