如何用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
投稿

猜你喜欢

  • SQL查询中需要使用别名问题

    2024-01-21 13:45:50
  • python的函数参数你了解吗

    2021-11-08 16:15:59
  • Python基于os.environ从windows获取环境变量

    2022-09-18 01:42:45
  • 个人网站与动网整合非官方方法

    2009-07-05 18:42:00
  • python实现简单遗传算法

    2022-12-14 00:33:21
  • Python单元测试简单示例

    2021-07-27 12:03:11
  • python实现搜索本地文件信息写入文件的方法

    2023-01-19 10:21:47
  • SqlServer 多种分页方式 详解(含简单速度测试)

    2024-01-15 07:28:04
  • php 常用字符串函数总结

    2024-06-05 09:47:33
  • SQL2005 服务器因重装改名后出错的说明

    2024-01-23 09:31:22
  • Python+pyftpdlib实现局域网文件互传

    2022-01-10 04:09:30
  • Python django导出excel详解

    2021-06-15 17:12:19
  • Python 之 Json序列化嵌套类方式

    2021-05-25 18:11:44
  • Google Chrome的hack写法以及CSS的支持程度

    2008-09-04 12:28:00
  • Python+MediaPipe实现检测人脸功能详解

    2021-05-05 15:34:17
  • Python中的并发编程asyncio库入门使用

    2021-02-13 05:03:52
  • tensorflow查看ckpt各节点名称实例

    2021-03-26 14:04:49
  • python中字典dict排序sorted的实现

    2023-02-20 13:21:45
  • tensorflow之变量初始化(tf.Variable)使用详解

    2023-08-19 06:46:46
  • vscode中配置jupyter的详细步骤(彻底解决Failed to start the Kernel问题)

    2022-06-21 06:56:26
  • asp之家 网络编程 m.aspxhome.com