您的位置:首页 > 脚本大全 > > 正文

python控制流实例(如何用C代码给Python写扩展库Cython)

更多 时间:2021-09-30 00:59:39 类别:脚本大全 浏览量:2305

python控制流实例

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

之前一篇文章里提到了利用cython来编译python,这次来讲一下如何用cython给python写扩展库。

两种语言混合编程,其中最重要的是类型的传递。

我们用一个简单的例子进行入门:这次的目标是用c语言写一个numpy的加法和元素相乘模块。在本例中,numpy的array被传入到c语言模块内,变成了二维数组。

1. 头文件main.h:

  • ?
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • #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中:

  • ?
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • #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噢):详细的知识点在注释中写出来了~

  • ?
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • # 既要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文件:

  • ?
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 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

    编译成功的图例:

    python控制流实例(如何用C代码给Python写扩展库Cython)

    此时在同目录下会生成“test.cp36-win_amd64.pyd”的二进制码文件,它是闭源的,但是可以直接用python来import。下面编写测试代码main.py来进行测试:

  • ?
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 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) # 输出运行结果
  • 执行结果:

    python控制流实例(如何用C代码给Python写扩展库Cython)

    通过本例我们可以看到:将循环放在c语言模块中,而不是原生的python中,可以提高执行效率。

    以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持开心学习网。

    原文链接:https://blog.csdn.net/qq_33353186/article/details/80298239

    您可能感兴趣