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. Python进阶(多线程)

    多线程结构 import threading def worker():#子线程要执行的具体逻辑代码函数 print('threading') t1 = threading.current_threa ...

  2. 分布式配置系统Apollo如何实时更新配置的?

    引言 记得我们那时候刚开始学习Java的时候都只是一个单体项目,项目里面的配置基本都是写在项目里面的properties文件中,比如数据库配置啥的,各种逻辑开关,一旦这些配置修改了,还需要重启项目这修 ...

  3. Semaphore信号量的使用

    package ThreadTest; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; pub ...

  4. buu misc 1-32 wp

    buuCTFwp(1~32) 1.签到题 题里就有flag flag{buu_ctf} 2.二维码 1.题目是一个二维码,用010发现提示四位数字,想到应该是暗藏压缩包 2.虚拟机foremost分离 ...

  5. JDK中Lambda表达式的序列化与SerializedLambda的巧妙使用

    前提 笔者在下班空余时间想以Javassist为核心基于JDBC写一套摒弃反射调用的轻量级的ORM框架,过程中有研读mybatis.tk-mapper.mybatis-plus和spring-boot ...

  6. 洛谷 P5527 - [Ynoi2012] NOIP2016 人生巅峰(抽屉原理+bitset 优化背包)

    洛谷题面传送门 一道挺有意思的题,想到了某一步就很简单,想不到就很毒瘤( 首先看到这样的设问我们显然可以想到背包,具体来说题目等价于对于每个满足 \(i\in[l,r]\) 的 \(a_i\) 赋上一 ...

  7. Codeforces 750E - New Year and Old Subsequence(线段树维护矩阵乘法,板子题)

    Codeforces 题目传送门 & 洛谷题目传送门 u1s1 我做这道 *2600 的动力是 wjz 出了道这个套路的题,而我连起码的思路都没有,wtcl/kk 首先考虑怎样对某个固定的串计 ...

  8. [Linux] 非root安装GCC9.1.0

    说明 一般Linux系统自带或公共的GCC版本都很低,如目前我们的服务器版本的GCC还停留在gcc-4.9.3,而官网已到达9.2版本(下载http://ftp.gnu.org/gnu/gcc/) , ...

  9. 【shell】循环将字符串写入数组中?

    bash shell脚本语法怪异,其他语言循环写入数组或列表都很简单实现,或有相应函数来做. 以下用两种方法来实现: 方法一 c=0 for i in `ls ./Data_Analysis/Quan ...

  10. SQL-关联表查询(连表查询)

    0.例如:select * from T1,T2     where T1.a=T2.a 1.连表查询 <=> join(inner join)内连接查询 数据源: Persion表:  ...