如何用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之Python安装
Sep 12 Python
详谈Python2.6和Python3.0中对除法操作的异同
Apr 28 Python
Django分页查询并返回jsons数据(中文乱码解决方法)
Aug 02 Python
Pandas Shift函数的基础入门学习笔记
Nov 16 Python
pygame游戏之旅 创建游戏窗口界面
Nov 20 Python
使用Flask-Cache缓存实现给Flask提速的方法详解
Jun 11 Python
python使用writerows写csv文件产生多余空行的处理方法
Aug 01 Python
Python使用微信接入图灵机器人过程解析
Nov 04 Python
win7上tensorflow2.2.0安装成功 引用DLL load failed时找不到指定模块 tensorflow has no attribute xxx 解决方法
May 20 Python
opencv 查找连通区域 最大面积实例
Jun 04 Python
如何教少儿学习Python编程
Jul 10 Python
python模拟浏览器 使用selenium进入好友QQ空间并留言
Apr 12 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
简单的php缓存类分享     php缓存机制
2014/01/22 PHP
8个PHP程序员常用的功能汇总
2014/12/18 PHP
php计算税后工资的方法
2015/07/28 PHP
PHP实现财务审核通过后返现金额到客户的功能
2019/07/04 PHP
javascript getElementsByClassName实现代码
2010/10/11 Javascript
JavaScript中使用正则匹配多条,且获取每条中的分组数据
2010/11/30 Javascript
加随机数引入脚本不让浏览器读取缓存
2014/09/04 Javascript
javascript伸缩型菜单实现代码
2015/11/16 Javascript
一篇文章掌握RequireJS常用知识
2016/01/26 Javascript
JavaScript中原型链存在的问题解析
2016/09/25 Javascript
mpvue中配置vuex并持久化到本地Storage图文教程解析
2018/03/15 Javascript
ES6基础之默认参数值
2019/02/21 Javascript
解决layer弹出层的内容页点击按钮跳转到新的页面问题
2019/09/14 Javascript
vuex实现数据状态持久化
2019/11/11 Javascript
Django静态资源URL STATIC_ROOT的配置方法
2014/11/08 Python
python使用socket进行简单网络连接的方法
2015/04/29 Python
Python使用xlrd模块操作Excel数据导入的方法
2015/05/26 Python
python 自定义异常和异常捕捉的方法
2018/10/18 Python
Python3远程监控程序的实现方法
2019/07/15 Python
Django框架HttpResponse对象用法实例分析
2019/11/01 Python
python中property和setter装饰器用法
2019/12/19 Python
opencv python如何实现图像二值化
2020/02/03 Python
Python中itertools的用法详解
2020/02/07 Python
python实现简单井字棋游戏
2020/03/04 Python
Python运行提示缺少模块问题解决方案
2020/04/02 Python
PyTorch-GPU加速实例
2020/06/23 Python
纯CSS3代码实现switch滑动开关按钮效果
2016/08/30 HTML / CSS
Marc Jacobs官方网站:美国奢侈品牌
2017/08/29 全球购物
房地产融资计划书
2014/01/10 职场文书
文科生自我鉴定
2014/02/15 职场文书
心理学专业大学生职业生涯规划范文
2014/02/19 职场文书
节电标语大全
2014/06/23 职场文书
销售人员工作自我评价
2014/09/21 职场文书
质量整改报告范文
2014/11/08 职场文书
活动简报范文
2015/07/22 职场文书
导游词之上海豫园
2019/10/24 职场文书