python调用第三方动态库(附代码)

本文介绍Python通过ctypes调用C/C++动态库的方法,包括动态库的加载、参数类型转换、结构体定义等内容,并给出具体实例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

摘要

本文讲述python混合编程之调用动态库

引言

python因为良好的编码性和扩展库正被大规模的使用,但他有两个缺点:1、代码可见;2、执行效率低,于是在实际应用中经常会把高效和核心代码用C/C++实现,业务部分用python实现。这就需要进行混合编程,本文对python调用动态库的方法及注意事项进行记录

主题

python标准库函数中提供了调用动态库的包————ctypes

加载动态库

  • 查找动态库ctypes.util.find_library
  • 根据动态库调用方式的不同,可以分为cdeclstdcall两种,这两种方式的主要区别见下表。后面的例子以cdecl调用方式为例,stdcall类同。
调用标准内存栈维护者函数名
cdecl调用者前面加下划线,后面加“@”符号和参数的字节数
stdcall被调用者在输出函数名前面加下划线

* ctypes加载动态库有两种方式。构造类对象libc = CDLL("libtestlib.dll")和实例化instancelibc = cdll.LoadLibrary("libtestlib.dll")。这两种方式都会返回一个动态库操作的句柄,以供后面使用

类型

  • 常规参数类型

C常规参数类型与ctypes、python类型对应关系表如下:

C typectypes typePython type
_Boolc_boolbool (1)
charc_char1-character bytes object
wchar_tc_wchar1-character string
charc_byteint
unsigned charc_ubyteint
shortc_shortint
unsigned shortc_ushortint
intc_intint
unsigned intc_uintint
longc_longint
unsigned longc_ulongint
__int64 or long longc_longlongint
unsigned __int64 or unsigned long longc_ulonglongint
size_tc_size_tint
ssize_t or Py_ssize_tc_ssize_tint
floatc_floatfloat
doublec_doublefloat
long doublec_longdoublefloat
char * (NUL terminated)c_char_pbytes object or None
wchar_t * (NUL terminated)c_wchar_pstring or None
void *c_void_pint or None

* 指针
* 弱引用指针byref(),这种方式速度更快
* 强指针pointer()
* c_types定义的指针c_char_pc_wchar_pc_void_p不可修改,如果需要在C函数中被修改,需要使用函数create_string_buffer()创建可变内存
* 用指针的value属性获取指针指向的内容

  • 结构体/联合体
    • 通过构建类继承Structure/Union来实现结构体的定义,把结构体属性组合成元组数组放在类中的_fields属性中。关于结构体的其它特性(对齐、指针嵌套、位域等)请参照官网

      class POINT(Structure):
      _fields_ = [("x", c_int),
      ("y", c_int)]
      class RECT(Structure):
      _fields_ = [("upperleft", POINT),
      ("lowerright", POINT)]
  • 数组直接使用类型 * 元素个数的方式来定义,如array = c_int * 10

  • 函数指针(回调函数)CFUNCTYPE

    • 使用注释的方式一次定义

      @CFUNCTYPE(c_int, c_int)
      def py_cb_func(a):
          print("py_cb_func", str(a))
          return a + 1
    • 标准方式

      
      # 定义c_types类型
      
      PY_CB_FUNC = CFUNCTYPE(c_int, c_int)
      def py_cb_func(a):
          print("py_cb_func", str(a))
          return a + 1
      cb_func = PY_CB_FUNC(py_cmp_func)

函数

  • 函数入参声明libtest.parm_int.argtypes = [c_int],做了函数声明后,会在python调用时对参数格式进行检查
  • 函数返回值类型声明libtest.parm_int.restype = c_int,做了函数声明后,会在python调用时对参数格式进行检查
  • 函数调用ret = libtest.parm_int(c_int(1))

实例

我专门针对c_types调用动态库写了一个实例。实例包括两个部分
- 用C实现的动态库代码。主要从入参类型、返回值类型、回调函数调用三个方面实现及提供接口
- 用python实现的对动态库调用代码。用于调用动态库的所有接口

附录

参考


### 如何在 Python 中集成和使用 C++ 第三方 #### 使用 `ctypes` 或者 `cffi` 对于简单的跨语言调用,可以考虑使用 Python 的内置模块 `ctypes` 或者更高级别的 `cffi` 来加载共享调用其中定义的功能。 ```python import ctypes # 加载动态链接 (.dll on Windows, .so on Linux) cpp_lib = ctypes.CDLL('./example.dll') # 定义函数原型 (假设有一个名为 add 的函数接受两个整数参数) cpp_lib.add.argtypes = [ctypes.c_int, ctypes.c_int] cpp_lib.add.restype = ctypes.c_int result = cpp_lib.add(10, 20) print(f"The result is {result}") ``` 这种方法适用于那些不依赖复杂数据结构或对象模型的情况[^1]。 #### 利用 SWIG 工具自动生成绑定代码 当涉及到更为复杂的交互,SWIG 是一种强大的工具,它能够自动为多种编程语言生成接口代码。通过编写一个接口文件(通常扩展名是.i),指定哪些部分应该暴露给目标语言,在这种情况下就是 Python。 ```swig %module example_module %{ #include "example.h" %} // 声明要导出的类或函数 int add(int a, int b); class MyClass { public: void doSomething(); }; ``` 之后运行命令行指令 `% swig -c++ example_module.i` 来创建必要的包装器源码,并将其编译成可被 Python 导入的形式[^2]。 #### 编写 Cython 扩展模块 另一种流行的方法是采用 Cython 技术。这允许开发者以类似于 Python 的语法书写接近原生性能级别的程序片段;这些片段会被转换成高效的 C/C++ 代码并与现有的 C++ 类型无缝对接。 ```cython # distutils: language=c++ from libcpp.vector cimport vector def sum_vector(vector[int]& v): """Sum elements of an integer vector.""" total = 0 for i in range(v.size()): total += v[i] return total ``` 为了使上述 Cython 函数可用作常规 Python 函数,还需要设置构建过程以便正确处理 `.pyx` 文件及其关联的头文件和其他资源[^3]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值