numpy ctypeslib 与 ctypes接口使用说明

作者:elfin


使用numpy.ctypelib或者ctypes库可以实现python直接待用C++。numpy.ctypeslib后端是基于ctypes实现的!所以接口是类似的。

如果只想看如何调用外部接口可以直接查看 1.4.3.1 动态链接库使用成功案例,其他部分有很多试错的过程!

Top --- Bottom


一、numpy.ctypeslib使用说明

numpy是python做计算非常基础的一个库,安装就直接使用pip install numpy即可。

导入python包import numpy.ctypeslib as ctl

1.1 准备好一个C++计算文件

#include <iostream>
using namespace std; int Paritition1(int A[], int low, int high) {
int pivot = A[low];
while (low < high) {
while (low < high && A[high] >= pivot) {
--high;
}
A[low] = A[high];
while (low < high && A[low] <= pivot) {
++low;
}
A[high] = A[low];
}
A[low] = pivot;
return low;
} void QuickSort(int A[], int low, int high) //快排母函数
{
if (low < high) {
int pivot = Paritition1(A, low, high);
QuickSort(A, low, pivot - 1);
QuickSort(A, pivot + 1, high);
}
} int main()
{
int arr[5] = { 9, 2, 8, -6, 5 };
QuickSort(arr, 0, 4);
for (int i = 0; i < 4; i++)
{
cout << arr[i] << " ";
}
cout << arr[4] << endl;
}

上面是一段C++版的快排代码。假设我们已经有这个cpp文件,那么如何在python中使用?

需要独立编译时,Windows系统可以在头文件中使用下面的语句

// __declspec(dllexport)  导出到库中(win系统环境下.dll文件链接宏)
extern "C" __declspec(dllexport) void QuickSort(int A[], int low, int high);

在Linux中需要使用extern进行声明

1.2 ctypeslib主要的五个接口

ctypeslib给我们提供了五个接口使用:

  • load_library加载c++文件;
  • ndpointer
  • c_intp
  • as_ctypes numpy数据类型转为ctypes类型
  • as_arrayc++数据转为numpy数据类型

Top --- Bottom

1.3 加载编译后的文件

我这里是在Windows上面测试,生成exe文件后可以直接读入。

c_quick = ctl.load_library(
libname="quick.exe",
loader_path=r"H:\quick\x64\Debug"
)

查看c_quick类型可以发现它是一个<class 'ctypes.CDLL'>类型。类型是没错的,但是_FuncPtr函数接收到QuickSort字符串时,却显示找不到这个函数;如果我们直接使用cpp,或者dll等文件,有显示: 不是有效的win32应用程序。

因为我们的执行环境是64位的,而visual studio是基于32位的,理论上有两种办法解决:

  • 将python程序改成32位版本,当然这种方法对我们来说一般难以接受;
  • 将外部链接编译为64位的,我在studio里面设置了x64,结果设置了一个寂寞。

为了不浪费太多时间,我将这部分搬到Ubuntu系统上进行测试。

1.4 Linux系统下加载编译后的文件

这里我们记录从文件生成到最终调用的全过程。

1.4.1 书写文档

$ sudo vim quik.c
# 将1.1的C++文件内容书写进文档并保持

1.4.2 编译、打包源文件

编译链接为可执行文件

$ sudo g++ -Wall quik.c -o quick
$ ./quick
-6 2 5 8 9

这里对main函数的默认数组进行了排序!

编译为目标文件

$ sudo g++ -Wall -c quik.c -o quick.o
$ ls
quick quick.o quik.c

生成静态链接库

$ sudo ar cr libquick.o quick.o
$ ls
libquick.o quick quick.o quik.c

生成动态链接库

$ sudo g++ -Wall -shared -fPIC quik.c -o libquick.so
$ ls
libquick.o libquick.so quick quick.o quik.c

Top --- Bottom

1.4.3 python加载外部链接库

我们尝试从不同的文件进行加载,分别是c++源文件、可执行文件、.o目标文件、外部静态链接库、外部动态链接库。

首先说明结果:c++源文件、可执行文件都不能被加载。

加载c++源文件报错:OSError: quik.c: invalid ELF header

加载可执行文件quick报错:OSError: no file with expected extension

加载目标文件报错:OSError: quick.o: only ET_DYN and ET_EXEC can be loaded

加载外部静态链接库报错:OSError: libquick.o: invalid ELF header

加载外部动态链接库报错:undefined symbol:“QuickSort”

现在我们只能使用main函数,其他函数调用会报未定义符号的错误,下面我们先基于so动态链接库走通这个流程。

1.4.3.1 动态链接库使用成功案例

首先我们模拟正常的项目,进行独立编译。我们将quik文件拆分为quik.cmain.c

问题的解决方案可以参考linux动态库so调用外部so,运行时出现undefined symbol

我们生成如下的quik.cmain.c文档:

#include <iostream>
using namespace std; //extern "C" C++中编译c格式的函数,如果利用c语言编译不需要
extern "C" void QuickSort(int A[], int low, int high); int Paritition1(int A[], int low, int high) {
int pivot = A[low];
while (low < high) {
while (low < high && A[high] >= pivot) {
--high;
}
A[low] = A[high];
while (low < high && A[low] <= pivot) {
++low;
}
A[high] = A[low];
}
A[low] = pivot;
return low;
} void QuickSort(int A[], int low, int high) //快排母函数
{
if (low < high) {
int pivot = Paritition1(A, low, high);
QuickSort(A, low, pivot - 1);
QuickSort(A, pivot + 1, high);
}
}
#include <iostream>
using namespace std; // 声明可以写 在 头文件中
extern "C" void QuickSort(int A[], int low, int high); int main(){
int arr[5] = { 9, 2, 8, -6, 5 };
QuickSort(arr, 0, 4);
for (int i = 0; i < 4; i++){
cout << arr[i] << " ";
}
cout << arr[4] << endl;
}

上面我们使用extern进行了QuickSort函数的声明,这样就可以不使用quik.h头文件,要注意的是声明中使用的是“C”进行编译防止函数名被优化!基于此项改动就可以得到如下效果

python调用so动态链接库

$ sudo g++ -Wall -c main.c
$ sudo g++ -Wall -c quik.c -o quick.o
$ sudo g++ -Wall -shared -fPIC main.c quik.c -o libquick.so
$ python
>>> import numpy.ctypeslib as ctl
>>> ctl_lib = ctl.load_library("libquick.so", "./")
>>> import numpy as np
>>> arr_1d = ctl.ndpointer(
... dtype=np.int32,
... ndim=1,
... flags="CONTIGUOUS"
... )
>>> ctl_lib.QuickSort.restypes = None
>>> from ctypes import c_int, c_float
>>> ctl_lib.QuickSort.argtypes = [arr_1d, c_int, c_int]
>>> arr1 = np.array([5, -6, 8, 4, 7, 6, 3], dtype=np.int32)
>>> ctl_lib.QuickSort(arr1, 0, len(arr1))
>>> print(arr1)
[-6 3 4 5 6 7 8]
>>> type(arr1)
<class 'numpy.ndarray'>

注: 关于最好还是写在声明头文件中,main.c文件引入头文件;但是函数定义文件中的声明是一定要有的!

动态库可以,静态库却不可以?

直接加载quik.c源文件生成的静态链接库确实是不行!即使我们已经声明还是会有invalid ELF header错误,也不难理解,声明是解决不了这个问题的。鉴于我们常使用动态链接库,静态库该如何调用留作悬念吧。不过根据这个报错,很可能我们调用不起来。

1.4.3.2 python调用外部链接库总结

  • 调用外部C/C++接口,我们最好使用动态链接库;

  • 动态链接库生成的时候一定要注意进行声明extern "C" void QuickSort(int A[], int low, int high);

    不然加载后会找不到函数名,这个名字会被优化成其他

  • 更多细节可以参考官方文档https://scipy-cookbook.readthedocs.io/items/Ctypes.html


Top --- Bottom

完!

ctypes与numpy.ctypeslib的使用的更多相关文章

  1. 小白眼中的AI之~Numpy基础

      周末码一文,明天见矩阵- 其实Numpy之类的单讲特别没意思,但不稍微说下后面说实际应用又不行,所以大家就练练手吧 代码裤子: https://github.com/lotapp/BaseCode ...

  2. python调用C++实例:用C++对numpy执行BFS(广度优先搜索)

    下文的代码可能展示不全,详情请下载文件:用cpp遍历ndarray.rar 问题背景: 现在我有一张二值图test.npy,需要对其闭区域进行孔洞填充,如下图所示: 文件下载链接:用cpp遍历ndar ...

  3. Python调用C/Fortran混合的动态链接库-下篇

    接着前面的内容,我们在这里继续介绍Python传递二维数组到fortran并进行简单计算后返回的例子. 问题描述: module py2f90 use,intrinsic::iso_c_binding ...

  4. [Python] 06 - Modules --> Packages

    故事背景 一.阶级关系 1. Programs are composed of modules.2. Modules contain statements.3. Statements contain ...

  5. python调用.so

    python调用动态链接库的基本过程 动态链接库在Windows中为.dll文件,在linux中为.so文件.以linux平台为例说明python调用.so文件的使用方法. 本例中默认读者已经掌握动态 ...

  6. Python调用C/Fortran混合的动态链接库--中篇

    接下来,介绍一个简单的例子,从fortran中传递并返回一维自定义结构体数组到python注意点:1.fortran新标准支持可分配数组作为变量传入并在subroutine或function分配后返回 ...

  7. Python调用C/Fortran混合的动态链接库--上篇

    内容描述: 在32位或64位的windows或GNU/Linux系统下利用Python的ctypes和numpy模块调用C/Fortran混合编程的有限元数值计算程序 操作系统及编译环境: 32bit ...

  8. python调用c++类方法(2)

    testpy.cpp: #include<iostream> #include<vector> struct point{ float pointx; float pointy ...

  9. 【转】Python调用C语言动态链接库

    转自:https://www.cnblogs.com/fariver/p/6573112.html 动态链接库在Windows中为.dll文件,在linux中为.so文件.以linux平台为例说明py ...

随机推荐

  1. 在Jenkins中执行 PowerShell 命令实现高效的CD/CI部署

    相比于cmd,powershell支持插件.语法扩展和自定义扩展名,是智能化部署中闪闪的新星,越来越多的开发者偏爱使用Powershell. 如何让Jenkins支持Powershell呢?本文即展开 ...

  2. 手把手教你基于Netty实现一个基础的RPC框架(通俗易懂)

    阅读这篇文章之前,建议先阅读和这篇文章关联的内容. [1]详细剖析分布式微服务架构下网络通信的底层实现原理(图解) [2][年薪60W的技巧]工作了5年,你真的理解Netty以及为什么要用吗?(深度干 ...

  3. Apache Kafka分布式流处理平台及大厂面试宝典v3.0.0

    概述 **本人博客网站 **IT小神 www.itxiaoshen.com 定义 Apache Kafka官网地址 http://kafka.apache.org/ 最新版本为 3.0.0 Apach ...

  4. Redis 持久化方案

    目录 持久化简介 什么是持久化? Redis 持久化方案 RDB RDB 简介 save 指令 操作与配置 工作原理 bgsave 指令 操作与配置 工作原理 bgsave 配置执行 相关配置 工作原 ...

  5. 【原】MDC日志链路设计

    背景 我们项目中现有日志系统,采用的是slf4j+logback这套日志组件,也是Java生态里面比较常用的一个日志组件,但是随着分布式的演进,这套组件明显存在以下几个问题: 1.各种无关日志穿行其中 ...

  6. 洛谷 P4240 - 毒瘤之神的考验(数论+复杂度平衡)

    洛谷题面传送门 先扯些别的. 2021 年 7 月的某一天,我和 ycx 对话: tzc:你做过哪些名字里带"毒瘤"的题目,我做过一道名副其实的毒瘤题就叫毒瘤,是个虚树+dp yc ...

  7. 洛谷 P5902 [IOI2009]salesman(dp)

    题面传送门 题意: 有 \(n\) 个展销会,每个展销会给出它的时间 \(t_i\),举办展销会的位置 \(l_i\),和参加这个展销会你能得到的收益 \(m_i\). 你现在在位置 \(s\),你可 ...

  8. Atcoder Grand Contest 030 F - Permutation and Minimum(DP)

    洛谷题面传送门 & Atcoder 题面传送门 12 天以前做的题了,到现在才补/yun 做了一晚上+一早上终于 AC 了,写篇题解纪念一下 首先考虑如果全是 \(-1\)​ 怎么处理.由于我 ...

  9. R包 tidyverse 分列

    代码: 1 library(tidyverse) 2 separate(data = df,col=chr_pos,into=c("chr","pos"),se ...

  10. 使用SpringBoot实现文件的上传

    使用SpringBoot实现文件的上传 springboot可以直接使用 org.springframework.web.multipart.MultipartFile 所以非常容易实现 一.首先是简 ...