如何用C代码给Python写扩展库(Cython)

作者:jazz_bin 时间:2023-06-08 17:06:32 

之前一篇文章里提到了利用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中,可以提高执行效率。

来源:https://blog.csdn.net/qq_33353186/article/details/80298239

标签:C代码,Python,扩展库,Cython
0
投稿

猜你喜欢

  • Python下的Softmax回归函数的实现方法(推荐)

    2022-02-13 10:42:17
  • PHP获取url的函数代码

    2023-10-15 12:45:00
  • 关于python中readlines函数的参数hint的相关知识总结

    2023-12-31 02:37:12
  • 全面理解Python中self的用法

    2022-10-09 07:16:50
  • asp操作Excel类源码

    2009-12-25 19:01:00
  • python 3.7.0 安装配置方法图文教程

    2023-10-14 22:24:28
  • 配置php网页显示各种语法错误

    2023-11-14 07:23:04
  • Javascript操作表单之间的数据传递

    2007-10-25 12:58:00
  • 交互设计师心得——核心竞争力

    2010-01-19 13:45:00
  • DIV+CSS设计时容易犯的一些错误

    2007-11-13 12:42:00
  • php对数字进行万、亿单位的转化

    2023-06-24 08:34:32
  • SQL Server数据库日志清除的两个方法

    2009-01-08 13:44:00
  • Django重装mysql后启动报错:No module named ‘MySQLdb’的解决方法

    2023-02-01 07:31:31
  • javascript new fun的执行过程

    2010-08-05 21:23:00
  • python性能测试手机号验证码登录压测示例详解

    2021-06-05 13:21:25
  • 网页布局设计基础

    2008-10-16 13:58:00
  • Python字典和集合讲解

    2021-03-05 22:31:14
  • 合并SQL脚本文件的方法分享

    2011-09-30 11:13:03
  • Python装饰器如何实现修复过程解析

    2023-11-22 04:27:43
  • pycharm使用正则表达式批量添加print括号完美从python2迁移到python3

    2021-11-06 15:30:35
  • asp之家 网络编程 m.aspxhome.com