如何用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 optparse模块使用实例
Apr 09 Python
在python的类中动态添加属性与生成对象
Sep 17 Python
读写json中文ASCII乱码问题的解决方法
Nov 05 Python
Python更新数据库脚本两种方法及对比介绍
Jul 27 Python
TensorFlow实现RNN循环神经网络
Feb 28 Python
Python生成器定义与简单用法实例分析
Apr 30 Python
PyCharm在新窗口打开项目的方法
Jan 17 Python
python微信聊天机器人改进版(定时或触发抓取天气预报、励志语录等,向好友推送)
Apr 25 Python
解决python 文本过滤和清理问题
Aug 28 Python
python生成器推导式用法简单示例
Oct 08 Python
python 第三方库paramiko的常用方式
Feb 20 Python
Django数据库(SQlite)基本入门使用教程
Jul 07 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中防止SQL注入攻击和XSS攻击的两个简单方法
2010/04/15 PHP
Codeigniter中mkdir创建目录遇到权限问题和解决方法
2014/07/25 PHP
php支付宝手机网页支付类实例
2015/03/04 PHP
文本框中,回车键触发事件的js代码[多浏览器兼容]
2010/06/07 Javascript
再说AutoComplete自动补全之实现原理
2011/11/05 Javascript
JQuery入门——事件切换之toggle()方法应用介绍
2013/02/05 Javascript
jQuery中的read和JavaScript中的onload函数的区别
2014/08/27 Javascript
jQuery中last()方法用法实例
2015/01/06 Javascript
jQuery常用数据处理方法小结
2015/02/20 Javascript
JQuery中上下文选择器实现方法
2015/05/18 Javascript
JS实现网页Div层Clone拖拽效果
2015/09/26 Javascript
浅析在javascript中创建对象的各种模式
2016/05/06 Javascript
浅析js绑定事件的常用方法
2016/05/15 Javascript
PassWord输入框代码分享
2016/06/07 Javascript
picLazyLoad 实现图片延时加载(包含背景图片)
2016/07/21 Javascript
jQuery实现的图片轮播效果完整示例
2016/09/12 Javascript
jQuery与js实现颜色渐变的方法
2016/12/30 Javascript
JavaScript定义函数_动力节点Java学院整理
2017/06/27 Javascript
前后端如何实现登录token拦截校验详解
2018/09/03 Javascript
Angular Material Icon使用详解
2018/11/07 Javascript
微信小程序日历效果
2018/12/29 Javascript
一起写一个即插即用的Vue Loading插件实现
2019/10/31 Javascript
JS几个常用的函数和对象定义与用法示例
2020/01/15 Javascript
[00:37]DOTA2上海特级锦标赛 Secert 战队宣传片
2016/03/03 DOTA
[14:03]2017DOTA2亚洲邀请赛开幕式:12神兵演绎水墨中华
2017/04/01 DOTA
[06:49]2018DOTA2国际邀请赛寻真——VirtusPro傲视群雄
2018/08/12 DOTA
[45:59]完美世界DOTA2联赛PWL S2 FTD vs GXR 第二场 11.22
2020/11/24 DOTA
python字典的常用操作方法小结
2016/05/16 Python
给排水工程师岗位职责
2013/11/21 职场文书
高级工程师英文求职信
2014/03/19 职场文书
关于运动会的广播稿
2014/09/22 职场文书
六查六看六改心得体会
2014/10/14 职场文书
创先争优活动个人总结
2015/03/04 职场文书
2015教师年度工作总结范文
2015/04/07 职场文书
pandas:get_dummies()与pd.factorize()的用法及区别说明
2021/05/21 Python
SQL Server 忘记密码以及重新添加新账号
2022/04/26 SQL Server