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. Part 29 AngularJS intellisense in visual studio

    In the previous videos if you have noticed as we were typing the angular code in Script.js file we w ...

  2. Python使用cx_Oracle模块操作Oracle数据库--通过sql语句和存储操作

    https://www.jb51.net/article/125160.htm?utm_medium=referral  Python使用cx_Oracle调用Oracle存储过程的方法示例 http ...

  3. OPA-Gatekeeper实验:对特定用户的更新时间窗口做限制

    实验目的 OPA-Gatekeeper可以在Kubernetes 中,通过策略来实现一些额外的管理.安全方面的限制,例如:限制特定用户在 Namespace 中的行为权限 本次实验将在test命名空间 ...

  4. Redis篇:事务和lua脚本的使用

    现在多数秒杀,抽奖,抢红包等大并发高流量的功能一般都是基于 redis 实现,然而在选择 redis 的时候,我们也要了解 redis 如何保证服务正确运行的原理 前言 redis 如何实现高性能和高 ...

  5. [loj2091]小星星

    (分别用$E_{T}$和$E_{G}$表示树和图的边集) 简单分析,可以发现题目即求排列$p_{i}$的数量,满足$\forall (x,y)\in E_{T},(p_{x},p_{y})\in E_ ...

  6. [bzoj5025]单调上升路径

    由于题目的证明可以发现$ans\ge 2m/n \ge n-1$,于是大胆猜测答案就是n-1若n是奇数,则将边分为n组,每组(n-1)/2,如果同组内边没有交点,那么只需要每一组边一个权值区间,从每一 ...

  7. pycharm的selenium设置

    如果运行文件,提示 no model named selenium 那就需要添加selenium的安装地址 如上图 在python>lib>site-packages 在pycharm的f ...

  8. uniapp增加自定义埋点功能

    起因 首先来说,uniapp其实是自带系统埋点统计功能的.基本也算是面面俱到. 但是一些未知原因,貌似数据有所丢失,再加上没有一些重要的定制化功能,以及最重要的数据安全方面的考虑,还是决定接入公司的埋 ...

  9. 【JAVA】编程(6)--- 应用IO流拷贝文件夹(内含多个文件)到指定位置

    此程序应用了: File 类,及其常用方法: FileInputStream,FileOutputStream类及其常用方法: 递归思维: package com.bjpowernode.javase ...

  10. 【树莓派】Python开发工控机急停设计

    背景 我们在一些工业产品中使用树莓派替代了PLC和上位机,并借助树莓派的算力将AI和机器视觉引入工业领域. 以前的产品都不存在动作机构,仅仅将结果输出到指示灯.蜂鸣器或者显示器上,没有安全隐患, 现在 ...