Python使用ctypes实现与C++互相调用的实践教程

 更新时间:2026年02月04日 08:37:29   作者:爱吃芒果的蘑菇  
这篇文章主要为大家详细介绍了Python如何使用ctypes实现与C++互相调用,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下

背景

小H上次使用了pybind11来调用C++的方法,这次同样是在项目中遇到了需要在py层调用C++方法的情况,现在对于性能的需求更加敏感,所以需要使用C++的底层方法来获得结果(当然也可能只是因为C++有这个方法,直接拿来用比较方便),选择ctypes应该也是因为这个比较方便吧。

示例

在C++侧,我们需要实现一个函数,然后导出一个动态库方法,这里楼主从py层传入了一个回调函数,返回了对应的字符结果。

在py侧,首先需要使用ctypes.CDLL加载刚才编译出的动态库,声明对应的方法,之后实现一个回调函数进行传入。

C++动态库实现

#include <iostream>
#include <string>

// 回调类型:void callback(int, const char*)
using CallbackType = void(*)(int, const char*);

extern "C" {

// 导出函数:循环调用回调
void run_with_callback(CallbackType cb, int count) {
    if (!cb) {
        std::cerr << "[C++] callback is null" << std::endl;
        return;
    }

    std::cout << "[C++] run_with_callback start, count = " << count << std::endl;

    for (int i = 0; i < count; ++i) {
        std::cout << "[C++] before calling callback, i = " << i << std::endl;

        std::string message = "msg from C++ index = " + std::to_string(i);
        cb(i, message.c_str());  // 回调到 Python

        std::cout << "[C++] after calling callback, i = " << i << std::endl;
    }

    std::cout << "[C++] run_with_callback end" << std::endl;
}

} // extern "C"

ps:C++ 默认会对函数名做 name mangling(名字改编),导致动态库中的符号名变复杂;ctypes按 C ABI 去找函数名,如果不显式用 extern "C",Python 侧很难直接按名字找到

用CMake编译成动态库

cmake_minimum_required(VERSION 3.10)
project(pyc_callback_demo LANGUAGES CXX)

add_library(mycallback SHARED callback_lib.cpp)

set_target_properties(mycallback PROPERTIES
    OUTPUT_NAME "mycallback"
    CXX_STANDARD 17
    CXX_STANDARD_REQUIRED YES
)

构建

mkdir -p build
cd build
cmake ..
cmake --build .

这样就得到了一个.so文件,供py侧加载。

python加载动态库并调用

  • 用 @CALLBACK_CTYPE 装饰 Python 函数,ctypes 会把它包装成 C 函数指针。
  • 字符串参数在 Python 中收到的是 bytes,需要手动解码
import ctypes
import pathlib
import os

# 当前文件所在目录
BASE_DIR = pathlib.Path(__file__).resolve().parent

LIB_PATH = BASE_DIR / "cpp_lib" / "build" / "libmycallback.so"

if not LIB_PATH.exists():
    raise FileNotFoundError(f"找不到动态库: {LIB_PATH}. 请先在 cpp_lib 目录下用 CMake 编译生成 libmycallback.so")

# 加载动态库
lib = ctypes.CDLL(str(LIB_PATH))

# 定义 C 端回调类型
# 第二个参数用 ctypes.c_char_p(C 端是 const char*)
CALLBACK_CTYPE = ctypes.CFUNCTYPE(None, ctypes.c_int, ctypes.c_char_p)

# 声明 C 函数的签名:void run_with_callback(CallbackType cb, int count)
lib.run_with_callback.argtypes = [CALLBACK_CTYPE, ctypes.c_int]
lib.run_with_callback.restype = None


# Python 侧的回调实现
@CALLBACK_CTYPE
def py_callback(i: int, msg: bytes) -> None:
    # msg 是 bytes,需要按合适编码解码,这里假设 UTF-8
    text = msg.decode("utf-8") if msg is not None else "<NULL>"
    print(f"[Python] in callback, i = {i}, msg = {text}")


def main() -> None:
    print("[Python] before calling C function")

    # 调用 C++ 库函数,并把 Python 回调传进去
    lib.run_with_callback(py_callback, 3)

    print("[Python] after calling C function")


if __name__ == "__main__":
    main()

ctypes与pybind11区别

ctypes:纯 Python 侧绑定

不需要在 C++ 里写任何“绑定代码”,只要提供 C ABI 的动态库。

Python 侧通过 ctypes.CDLL、CFUNCTYPE 等描述函数签名。

只要函数满足“C 风格接口”(例如 extern "C" + 基本类型/指针),不在意具体是由 C 还是 C++ 实现。

非常适合:

  • 已经有现成 C 库 / C 接口的 C++ 库
  • 简单函数调用、回调、不复杂的数据结构

pybind11:在 C++ 侧写绑定,生成 Python 模块

是一个头文件库,写法大致像如之前提供的示例一致

编译后得到的是一个 Python 扩展模块(.so),import mymodule 就像普通 Python 包一样使用。

可以非常自然地暴露:

  • C++ 类、方法、构造函数
  • STL 容器(std::vectorstd::map 等)
  • 智能指针、异常、枚举等

对复杂 C++ API 的封装能力远强于 ctypes。

结语

经过这两次学习,让小H更加能够体会到在代码的世界中,语言并不是孤立的,弱化了对语言的重视程度,学习编程更应该将编程语言看作一种工具,什么时候什么情况该换就换,大家各自完成各自擅长的部分,更有利于找到性能与效率的均衡点。

到此这篇关于Python使用ctypes实现与C++互相调用的实践教程的文章就介绍到这了,更多相关Python与C++互调内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Django框架 查询Extra功能实现解析

    Django框架 查询Extra功能实现解析

    这篇文章主要介绍了Django框架 查询Extra功能实现解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-09-09
  • Python中pyCirclize包的使用教程详解

    Python中pyCirclize包的使用教程详解

    pyCirclize 是一个 Python 包,用于绘制环形图,这篇文章将将提供更丰富的示例代码,以更全面地展示 pyCirclize 包的功能和用法,感兴趣的小伙伴可以了解一下
    2023-11-11
  • Python数组条件过滤filter函数使用示例

    Python数组条件过滤filter函数使用示例

    数组条件过滤简洁实现方式,使用filter函数,实现一个条件判断函数即可,示例代码如下
    2014-07-07
  • Python将Word文档转换为Markdown格式

    Python将Word文档转换为Markdown格式

    Markdown作为一种轻量级标记语言,以其简洁的语法和广泛的兼容性,本文将介绍如何使用Python将Word文档转换为Markdown文件,需要的可以了解下
    2024-11-11
  • 对pandas中两种数据类型Series和DataFrame的区别详解

    对pandas中两种数据类型Series和DataFrame的区别详解

    今天小编就为大家分享一篇对pandas中两种数据类型Series和DataFrame的区别详解,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-11-11
  • Python使用Matplotlib绘制甘特图的实践

    Python使用Matplotlib绘制甘特图的实践

    甘特图已经发展成项目规划和跟踪的必备工具,本文主要介绍了Python使用Matplotlib绘制甘特图的实践,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-12-12
  • 超详细注释之OpenCV构建透明的叠加层

    超详细注释之OpenCV构建透明的叠加层

    这篇文章主要介绍了OpenCV构建透明的叠加层,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-09-09
  • python如何修改图像的分辨率

    python如何修改图像的分辨率

    这篇文章主要介绍了python如何修改图像的分辨率问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-11-11
  • Python 3中的yield from语法详解

    Python 3中的yield from语法详解

    在python 3.3里,generator新增了一个语法 yield from,这个yield from的作用是什么?语法是什么呢?下面通过这篇文章主要给大家详细介绍了Python 3中yield from语法的相关资料,需要的朋友可以参考借鉴,下面来一起看看吧。
    2017-01-01
  • 利用Python构建Flutter应用的教程详解

    利用Python构建Flutter应用的教程详解

    Flutter在软件研发领域是非常流行的,今天就让我们深入了解一下,用 Python构建flutter应用程序的世界,感兴趣的小伙伴可以跟随小编一起了解一下
    2022-12-12

最新评论