如何用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中使用语句导入模块或包的机制研究
Mar 30 Python
分析在Python中何种情况下需要使用断言
Apr 01 Python
深入探究Python中变量的拷贝和作用域问题
May 05 Python
使用Python实现BT种子和磁力链接的相互转换
Nov 09 Python
Python中方法链的使用方法
Feb 23 Python
Python爬虫之正则表达式基本用法实例分析
Aug 08 Python
对python3标准库httpclient的使用详解
Dec 18 Python
Python爬虫beautifulsoup4常用的解析方法总结
Feb 25 Python
Python 编程速成(推荐)
Apr 15 Python
python绘制随机网络图形示例
Nov 21 Python
python 字段拆分详解
Dec 17 Python
python中uuid模块实例浅析
Dec 29 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中常量,变量的作用域和生存周期
2013/08/10 PHP
一组PHP加密解密函数分享
2014/06/05 PHP
php 的多进程操作实践案例分析
2020/02/28 PHP
iframe 父窗口和子窗口相互的调用方法集锦
2010/12/15 Javascript
document.write的几点使用心得
2014/05/14 Javascript
QQ空间顶部折页撕开效果示例代码
2014/06/15 Javascript
JS实现网站菜单拖拽移位效果的方法
2015/09/24 Javascript
javascript实现简单计算器效果【推荐】
2016/04/19 Javascript
深入理解JavaScript中的浮点数
2016/05/18 Javascript
原生JS实现图片轮播与淡入效果的简单实例
2016/08/21 Javascript
AngularJS监听路由的变化示例代码
2016/09/23 Javascript
Bootstrap基本组件学习笔记之缩略图(13)
2016/12/08 Javascript
浅谈vue中.vue文件解析流程
2018/04/24 Javascript
vue用递归组件写树形控件的实例代码
2018/07/19 Javascript
Vue proxyTable配置多个接口地址,解决跨域的问题
2020/09/11 Javascript
[43:43]完美世界DOTA2联赛PWL S2 LBZS vs Forest 第三场 11.29
2020/12/02 DOTA
Python多线程结合队列下载百度音乐的方法
2015/07/27 Python
python使用正则表达式的search()函数实现指定位置搜索功能
2017/11/10 Python
Python实现螺旋矩阵的填充算法示例
2017/12/28 Python
对pandas写入读取h5文件的方法详解
2018/12/28 Python
详解用python生成随机数的几种方法
2019/08/04 Python
python os.path.isfile 的使用误区详解
2019/11/29 Python
Python3.5 win10环境下导入kera/tensorflow报错的解决方法
2019/12/19 Python
详解python itertools功能
2020/02/07 Python
Python如何实现线程间通信
2020/07/30 Python
python编写扎金花小程序的实例代码
2021/02/23 Python
CSS3制作炫酷带方向感应的鼠标滑过图片3D动画
2016/03/16 HTML / CSS
最好的意大利皮夹克:D’Arienzo
2018/12/04 全球购物
介绍一下Linux内核的排队自旋锁
2014/01/04 面试题
绩效管理实施方案
2014/03/19 职场文书
五一活动标语
2014/06/30 职场文书
税务会计岗位职责
2015/04/02 职场文书
红与黑读书笔记
2015/06/29 职场文书
2015年汽车销售员工作总结
2015/07/24 职场文书
竞聘书的秘诀
2019/04/02 职场文书
go语言求任意类型切片的长度操作
2021/04/26 Golang