[Python]pytorch与C交互

文章目录

    • C++库
    • ctypes
      • 基础数据类型
      • 参数与返回值类型
      • 数组
      • 指针
      • 结构体类型
      • 回调函数
      • 工具函数
    • 示例

ctypes是Python的外部函数,提供了与C兼容的类型,并允许调用DLL库中的函数。

C++库

要使函数能被Python调用,需要编译为动态库:

# -fPIC使得位置独立
# -shared代表这是动态库
g++ -fPIC -shared -o libTest.so test.cpp

为保证函数接口能被外部识别,需要导出为纯C的:

#ifdef __cplusplus
extern "C"
{
#endif


void * callForTest(char *params);


#ifdef __cplusplus
};
#endif

ctypes

在python中要使用DLL库,需要先通过cdll来加载(cdll载入按标准的 cdecl调用协议导出的函数):

from ctypes import cdll

target = cdll.LoadLibrary("libTest.so")

通过in_dll()可获取库中导出的变量:

# 获取 Python 库本身的 Py_OptimizeFlag
opt_flag = c_int.in_dll(pythonapi, "Py_OptimizeFlag")

基础数据类型

ctypes 定义了一些和C兼容的基本数据类型,所有基础类型都继承自ctypes._SimpleCData

  • value属性:包含实例的实际值。实例提取 value 属性时,通常每次会返回一个新的对象。
ctypes 类型C 类型Python 类型
c_bool_Boolbool (1)
c_charchar1-character bytes
c_wcharwchar_t1-character str
c_bytecharint
c_ubyteunsigned charint
c_shortshortint
c_ushortunsigned shortint
c_intintint
c_uintunsigned intint
c_longlongint
c_ulongunsigned longint
c_longlong__int64 or long longint
c_ulonglongunsigned __int64 or unsigned long longint
c_size_tsize_tint
c_ssize_tssize_t or Py_ssize_tint
c_floatfloatfloat
c_doubledoublefloat
c_longdoublelong doublefloat
c_char_pchar* (NUL terminated)bytes or None
c_wchar_pwchar_t* (NUL terminated)str or None
c_void_pvoid*int or None

除了整数、字符串以及字节串之外,所有的Python类型都必须使用它们对应的ctypes类型包装,才能够被正确地转换为所需的C语言类型。通过ctypes创建的类型是可变的(通过修改.value):

i = c_int()
# i.value == 0
i.value = 99
# i.value == 99

c_wchar_p("Hello, World")
# c.value == "Hello, World"

当给指针类型的对象c_char_p, c_wchar_p 和 c_void_p等赋值时,将改变它们所指向的内存地址,而不是它们所指向的内存区域的内容。若底层函数可能会改变指针地址,则需要通过create_string_buffer创建:

p = create_string_buffer(3) # create a 3 byte buffer, initialized to NUL bytes

p = create_string_buffer(b"Hello") # create a buffer containing a NUL terminated string

p = create_string_buffer(b"Hello", 10) # create a 10 byte buffer
p.value = b"Hi"
print(sizeof(p), repr(p.raw))
# 10 b'Hi\x00lo\x00\x00\x00\x00\x00'

参数与返回值类型

以libc库为例:

cdll.LoadLibrary("libc.so.6")

printf = libc.printf
# 通过设置argtypes属性来指定函数的必选参数类型
# 指定数据类型可以防止不合理的参数传递,并且会自动尝试将参数转换为需要的类型(否则必须手动转换)
printf.argtypes = [c_char_p, c_char_p, c_int, c_double]
printf(b"String '%s', Int %d, Double %f\n", b"Hi", 10, 2.2)

strchr = libc.strchr
# 默认返回int,其他类型需通过restype属性来设定
strchr.restype = c_char_p
strchr.argtypes = [c_char_p, c_char]
strchr(b"abcdef", b"d")

# 构造缓冲区,获取输出
i = c_int()
f = c_float()
s = create_string_buffer(b'\x00' * 32) # 创建长度32,全部为NULL的缓冲区
libc.sscanf(b"1 3.14 Hello", b"%d %f %s", byref(i), byref(f), s)
print(i.value, f.value, repr(s.value))
# 1 3.140000104904175 b'Hello'

数组

数组是一个序列,包含指定个数元素,且必须类型相同。创建数组类型的推荐方式是使用一个类型乘以一个正数:

  • _length_属性:指明数组中元素数量的正整数。
  • _type_属性:指明每个元素的类型。
class POINT(Structure):
  _fields_ = ("x", c_int), ("y", c_int)

pointsArray = POINT * 5
print(sizeof(pointsArray))
# 40

pa = pointArray(POINT(1,2), POINT(3,4)) # 后面三个全为0
print(sizeof(pa), len(pa))
# 40 5
for i in pa: print(i.x, i.y, end=";")
# 1 2;3 4;0 0;0 0;0 0;

指针

可以将ctypes类型数据传入pointer()函数创建指针:

  • contents属性:返回指针指向的真实对象( 每次访问这个属性时都会构造返回一个新的相同对象
  • _type_属性:指明所指向的类型。
  • 指针对象也可以通过整数下标进行访问;
  • 通过整数下标赋值可以改变指针所指向的真实内容;
  • 无参调用指针类型可以创建一个NULL指针;
i = c_int(12)
pi = pointer(i)
ic = pi.contents

print(ic, ic is i)
# c_int(12) False

# 通过下标访问与修改内容
pi[0]=34
print(pi[0], pi.contents)
# 34 c_int(34)

# 修改指针指向
ii = c_int(45)
pi.contents = ii
print(pi[0])
# 56

结构体类型

结构体必须通过子类化ctypes.Structure来创建,并且至少要定义一个 _fields_类变量,并允许通过直接属性访问来读取和写入字段。

  • _fields_属性:定义结构体字段的序列。 其中的条目必须为2元组或3元组。
    • 第一项是字段名称;
    • 第二项指明字段类型,可以是任何 ctypes 数据类型;
    • 对于整数类型字段(如c_int),可以给定第三个可选项;为定义字段比特位宽度的小正整数。
  • _pack_属性:一个可选的小整数,它允许覆盖实体中结构体字段的对齐方式。

对于不完整类型:即在结构体中包含指向自身的指针:

struct cell; /* forward declaration */

struct cell {
    char *name;
    struct cell *next;
};

在python中不能在类中使用自身,需要先定义结构,然后在设定_fields_属性:

class cell(Structure):
  pass

cell._fields_ = [("name", c_char_p),
                ("next", pointer(cell))]

回调函数

必须先为回调函数创建一个类(明确调用约定,返回值类型以及参数信息);CFUNCTYPE()工厂函数使用 cdecl 调用约定创建回调函数类型。

qsort = libc.qsort
qsort.restype=None

# 第一个参数为返回值类型,后续一次为对应函数参数
CMPFun = CFUNCTYPE(c_int, POINTER(c_int), POINTER(c_int))
def py_cmp_func(a,b):
  print('py_cmp_func', a[0], b[0])
  return 0

cmp_fun = CMPFun(py_cmp_func)

IntArray5 = c_int * 5
ia = IntArray5(1, 9, 7, 5, 8)
qsort(ia, len(ia), sizeof(c_int), py_cmp_func)

注意:回调函数是在Python之外的另外一个线程使用(外部代码调用这个回调函数), ctypes 会在每一次调用时创建一个虚拟 Python 线程。这个行为在大多数情况下是合理的,但也意味着如果有数据使用 threading.local 方式存储,将无法访问,就算它们是在同一个C线程中调用的。

工具函数

一些常用工具函数:

  • ctypes.addressof(obj):以整数形式返回内存缓冲区地址;obj 必须为一个 ctypes 类型的实例。
  • ctypes.alignment(obj_or_type):返回一个 ctypes 类型的对齐要求;obj_or_type 必须为一个 ctypes 类型或实例。
  • ctypes.byref(obj[, offset]):返回指向obj的引用,该对象必须为一个 ctypes 类型的实例。 offset 默认值为零,且必须为一个将被添加到内部指针值的整数。
  • ctypes.cast(obj, type):类似于 C 的强制转换运算符。 它返回一个 type 的新实例,该实例指向与 obj 相同的内存块。 type 必须为指针类型,而 obj 必须为可以被作为指针来解读的对象。
  • ctypes.create_string_buffer(init_or_size, size=None):创建一个可变的字符缓冲区。 返回的对象是一个 c_char 的 ctypes 数组。
    • init_or_size 必须是一个指明数组大小的整数,或者是一个将被用来初始化数组条目的字节串对象。
    • 如果将一个字节串对象指定为第一个参数,则将使缓冲区大小比其长度多一项以便数组的最后一项为一个 NUL 终结符。 可以传入一个整数作为第二个参数以允许在不使用字节串长度的情况下指定数组大小。
  • ctypes.create_unicode_buffer(init_or_size, size=None):创建一个可变的 unicode 字符缓冲区。
  • ctypes.get_errno():返回调用线程中系统 errno 变量的 ctypes 私有副本的当前值。
  • ctypes.memmove(dst, src, count):与标准 C memmove 库函数相同:将 count 个字节从 src 拷贝到 dst。 dst 和 src 必须为整数或可被转换为指针的 ctypes 实例。
  • ctypes.memset(dst, c, count):与标准 C memset 库函数相同:将位于地址 dst 的内存块用 count 个字节的 c 值填充。 dst 必须为指定地址的整数或 ctypes 实例。
  • ctypes.POINTER(type):创建并返回一个新的 ctypes 指针类型。 指针类型会被缓存并在内部重用,因此重复调用此函数耗费不大。 type 必须为 ctypes 类型。
  • ctypes.pointer(obj):创建一个新的指向 obj 的指针实例。 返回的对象类型为 POINTER(type(obj))。
    • 如果你只是想向外部函数调用传递一个对象指针,你应当使用更为快速的 byref(obj)。
  • ctypes.sizeof(obj_or_type):返回 ctypes 类型或实例的内存缓冲区以字节表示的大小。
  • ctypes.string_at(address, size=- 1):返回从内存地址 address 开始的以字节串表示的 C 字符串。 如果指定了 size,则将其用作长度,否则将假定字符串以零值结尾。
  • ctypes.wstring_at(address, size=- 1):返回从内存地址 address 开始的以字符串表示的宽字节字符串。

示例

以C++回调一个python函数为例:

C++中的回调定义:

#ifdef __cplusplus
extern "C"
{
#endif

typedef void (*PrintOutput)(const char* outputs);

void set_callback(PrintOutput func);

#ifdef __cplusplus
};
#endif

python中使用回调:

from ctypes import cdll, c_char_p, CFUNCTYPE, POINTER

target = cdll.LoadLibrary("/workspace/libTest.so")


PrintCallback = CFUNCTYPE(None, c_char_p)
def print_callback(outputs):
    print("outputs:", outputs)

py_callback = PrintCallback(print_callback)

target.set_callback.restype = None
target.set_callback(py_callback)    

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/81631.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【Android】设置-显示-屏保-启用时机-默认选中“一律不“

设置-屏保-启用时机-默认选中"一律不" 解决步骤(1)理清思路(2)过程(3)效果图 解决步骤 (1)理清思路 操作步骤: 首先手机进入设置—》点进显示选项—》进入后…

D. Anton and School - 2

范德蒙德恒等式 考虑统计每一个右括号位置的贡献&#xff0c;也就是每个右括号作为右边起点的贡献 其中i0的时候&#xff0c;r-1<r-0,故i0时贡献为0&#xff0c;直接套用恒等式不会有影响 #include <bits/stdc.h> using namespace std; typedef long long int ll; # d…

分布式锁实现方式

分布式锁 1 分布式锁介绍 1.1 什么是分布式 一个大型的系统往往被分为几个子系统来做&#xff0c;一个子系统可以部署在一台机器的多个 JVM(java虚拟机) 上&#xff0c;也可以部署在多台机器上。但是每一个系统不是独立的&#xff0c;不是完全独立的。需要相互通信&#xff…

【数据结构OJ题】有效的括号

原题链接&#xff1a;https://leetcode.cn/problems/valid-parentheses/ 目录 1. 题目描述 2. 思路分析 3. 代码实现 1. 题目描述 2. 思路分析 这道题目主要考查了栈的特性&#xff1a; 题目的意思主要是要做到3点匹配&#xff1a;类型、顺序、数量。 题目给的例子是比较…

神经网络基础-神经网络补充概念-02-逻辑回归

概念 逻辑回归是一种用于二分分类问题的统计学习方法&#xff0c;尽管名字中带有"回归"一词&#xff0c;但实际上它用于分类任务。逻辑回归的目标是根据输入特征来预测数据点属于某个类别的概率&#xff0c;然后将概率映射到一个离散的类别标签。 逻辑回归模型的核…

Django实现音乐网站 ⑾

使用Python Django框架制作一个音乐网站&#xff0c; 本篇主要是前端开发前的一些必要配置和首页展示开发。 目录 配置应用路由 创建应用路由文件 应用路径加入项目路径 创建项目模板 创建项目及应用模板路径 设置模板路径 设置静态资源路径 创建静态资源路径 配置静态…

Qt安卓开发经验技巧总结V202308

01&#xff1a;01-05 pro中引入安卓拓展模块 QT androidextras 。pro中指定安卓打包目录 ANDROID_PACKAGE_SOURCE_DIR $$PWD/android 指定引入安卓特定目录比如程序图标、变量、颜色、java代码文件、jar库文件等。 AndroidManifest.xml 每个程序唯一的一个全局配置文件&…

webshell实践,在nginx上实现负载均衡

1、配置多台虚拟机&#xff0c;用作服务器 在不同的虚拟机上安装httpd服务 我采用了三台虚拟机进行服务器设置&#xff1a;192.168.240.11、192.168.240.12、192.168.240.13 [rootnode0-8 /]# yum install httpd -y #使用yum安装httpd服务#开启httpd服务 [rootnode0-8 /]# …

【C#学习笔记】C#特性的继承,封装,多态

文章目录 封装访问修饰符静态类和静态方法静态构造函数 继承继承原则sealed修饰符里氏替换原则继承中的构造函数 多态接口接口的实例化 抽象类和抽象方法抽象类和接口的异同 虚方法同名方法new覆盖的父类方法继承的同名方法 运行时的多态性编译时的多态性 照理继承封装多态应该…

CSS 字体修饰属性

前言 字体修饰属性 属性说明font-family指定文本显示字体font-size设置字体的大小font-weight设置字体的粗细程度font-style设置字体的倾斜样式text-decoration给文本添加装饰线text-indent设置文本的缩进text-align设置文本的对齐方式line-height设置行高color设置文本的颜色…

Shell脚本基础教程

Shell脚本基础教程 Shell参数定义 定义变量 想要定义变量&#xff0c;只需要使用如下命令即可。 variable_namevariable_valuevariable_name表示变量名&#xff0c;variable_value表示变量值。注意&#xff0c;等号与变量名和变量值之间不能有空格。 变量名的命名需要遵循…

C语言入门_Day7 逻辑运算

目录&#xff1a; 前言 1.逻辑运算 2.优先级 3.易错点 4.思维导图 前言 算术运算用来进行数据的计算和处理&#xff1b;比较运算是用来比较不同的数据&#xff0c;进而来决定下一步怎么做&#xff1b;除此以外还有一种运算叫做逻辑运算&#xff0c;它的应用场景也是用来影…

电脑远程接入软件可以进行文件传输吗?快解析内网穿透

电脑远程接入软件的出现&#xff0c;让我们可以在两台电脑之间进行交互和操作。但是&#xff0c;很多人对于这些软件能否进行文件传输还存在一些疑问。下面的文章将解答这个问题。 1.电脑远程接入软件可以进行文件传输。传统上&#xff0c;我们可能会通过传输线或者移动存储设…

Redis在Java中的基本使用

本片将介绍 Redis 在 Java 中的基本使用 文章目录 1、使用jedis操作redis1.1、Jedis简介1.2、引入jedis的Maven依赖1.2、获取连接1.3、使用实例 2、对于JedisPooled的使用2.1、使用JedisPooled2.2、关于连接池 3、SpringBoot下使用Redis3.1、引入Maven依赖3.2、配置Redis连接3.…

RabbitMq:Topic exchange(主题交换机)的理解和使用

RabbitMq:Topic exchange(主题交换机)的理解和使用 在RabbitMq中&#xff0c;生产者的消息都是通过交换机来接收&#xff0c;然后再从交换机分发到不同的队列中去&#xff0c;在分发的过程中交换机类型会影响分发的逻辑&#xff0c;下面主要讲解一下主题交换机。 ​ 主题交换…

pycharm上传项目到github,版本管理

前提&#xff1a;下载git 设置Git路径 登录Github 此时自动打开浏览器&#xff0c;并打开连接页面&#xff0c;点击 Authorize GitHub。登录&#xff1a; 创建本地仓库 提交到Github 填写初始提交相关信息 origin&#xff0c;它们只是远程服务器的一个别名&#xff0c;否则你就…

NPM 创建和管理组织

目录 1、创建一个组织 2、将用户帐户转换为组织 3、组织中开启双因素身份验证 3.1 关于组织的双因素身份验证 3.2 先决条件 3.3 在您的组织中要求双因素身份验证 3.4 帮助已删除的成员和外部协作者重新加入您的组织 4、重命名组织 5、删除组织 1、创建一个组织 任何n…

“Spring管理JavaBean的过程及Bean的生命周期“

目录 引言1.弹簧容器2. Bean的生命周期2.1 配置javaBean2.2. 解析Bean的定义2.3 检查是否需要添加自己的功能2.4 初始化2.5 实现Aware接口2.6 扩展2.7. 销毁 3. 单例模式和原型模式3.1. 单例模式3.2. 原型模式 4. 总结 引言 Spring框架是一个非常流行的Java应用程序框架&#…

【【verilog典型电路设计之流水线结构】】

verilog典型电路设计之流水线结构 下图是一个4位的乘法器结构&#xff0c;用verilog HDL 设计一个两级流水线加法器树4位乘法器 对于流水线结构 其实需要做的是在每级之间增加一个暂存的数据用来存储 我们得到的东西 我们一般来说会通过在每一级之间插入D触发器来保证数据的联…

【AIGC】 快速体验Stable Diffusion

快速体验Stable Diffusion 引言一、安装二、简单使用2.1 一句话文生图2.2 详细文生图 三、进阶使用 引言 stable Diffusion是一款高性能的AI绘画生成工具&#xff0c;相比之前的AI绘画工具&#xff0c;它生成的图像质量更高、运行速度更快&#xff0c;是AI图像生成领域的里程碑…
最新文章