如何用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中的urllib模块使用详解
Jul 07 Python
python开发之thread线程基础实例入门
Nov 11 Python
解决Python中字符串和数字拼接报错的方法
Oct 23 Python
Python实现的文本简单可逆加密算法示例
May 18 Python
python读取中文txt文本的方法
Apr 12 Python
Python字典的核心底层原理讲解
Jan 24 Python
python数据类型之间怎么转换技巧分享
Aug 20 Python
python 类之间的参数传递方式
Dec 20 Python
windows下Pycharm安装opencv的多种方法
Mar 05 Python
Django中的session用法详解
Mar 09 Python
Pycharm内置终端及远程SSH工具的使用教程图文详解
Mar 19 Python
PyQT5 实现快捷键复制表格数据的方法示例
Jun 19 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 伪静态隐藏传递参数名的四种方法
2010/02/22 PHP
php_screw 1.5:php加密: 安装与使用详解
2013/06/20 PHP
javascript 关闭IE6、IE7
2009/06/01 Javascript
关于COOKIE个数与大小的问题
2011/01/17 Javascript
获取div编辑框,textarea,input text的光标位置 兼容IE,FF和Chrome的方法介绍
2012/11/08 Javascript
细说javascript函数从函数的构成开始
2013/08/29 Javascript
JS复制到剪贴板示例代码
2013/10/30 Javascript
JavaScript 事件绑定及深入
2015/04/13 Javascript
微信小程序 setData的使用方法详解
2017/04/20 Javascript
mockjs,json-server一起搭建前端通用的数据模拟框架教程
2017/12/18 Javascript
Vue.js组件实现选项卡以及切换特效
2019/07/24 Javascript
微信小程序自定义头部导航栏和导航栏背景图片 navigationStyle问题
2019/07/26 Javascript
解决Vue调用springboot接口403跨域问题
2019/09/02 Javascript
Node.js中console.log()输出彩色字体的方法示例
2019/12/01 Javascript
Vue使用axios引起的后台session不同操作
2020/08/14 Javascript
Echarts在Taro微信小程序开发中的踩坑记录
2020/11/09 Javascript
使用Typescript开发微信小程序的步骤详解
2021/01/12 Javascript
python 文件和路径操作函数小结
2009/11/23 Python
python执行外部程序的常用方法小结
2015/03/21 Python
Python对列表中的各项进行关联详解
2017/08/15 Python
Python线程指南分享
2019/11/19 Python
Python 私有化操作实例分析
2019/11/21 Python
python 变量初始化空列表的例子
2019/11/28 Python
Python基础之变量基本用法与进阶详解
2020/01/03 Python
python实现word文档批量转成自定义格式的excel文档的思路及实例代码
2020/02/21 Python
Python tkinter实现简单加法计算器代码实例
2020/05/13 Python
Python代码注释规范代码实例解析
2020/08/14 Python
加拿大约会网站:EliteSingles.ca
2018/01/12 全球购物
施华洛世奇新加坡官网:SWAROVSKI新加坡
2020/10/06 全球购物
出纳的岗位职责
2013/11/09 职场文书
如何写自我鉴定
2014/03/19 职场文书
争当四好少年演讲稿
2014/09/13 职场文书
幼儿教师师德师风自我剖析材料
2014/09/29 职场文书
小班教师个人总结
2015/02/05 职场文书
iPhone13再次曝光
2021/04/15 数码科技
jQuery实现广告显示和隐藏动画
2021/07/04 jQuery