python调用C++库实现数据类型转换的完整指南

 更新时间:2026年03月17日 09:22:52   作者:倔强老吕  
这篇文章主要为大家详细介绍了python如何调用C++库实现数据类型转换,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下

枚举类型的封装

假设有一个简单的枚举类型 Color,可以直接将其暴露给 Python。

示例代码 

#include <pybind11/pybind11.h>
namespace py = pybind11;
// 定义一个枚举类型
enum class Color { Red, Green, Blue };
PYBIND11_MODULE(example, m) {
    // 绑定枚举类型到 Python
    py::enum_<Color>(m, "Color")
        .value("Red", Color::Red)
        .value("Green", Color::Green)
        .value("Blue", Color::Blue)
        .export_values();  // 使枚举值可以直接通过模块访问
}

在 Python 中使用:

import example
print(example.Color.Red)  # 输出: Color.Red
print(int(example.Color.Green))  # 输出: 1

结构体的封装

接下来,看一个稍微复杂的例子,包括如何封装结构体和带有构造函数的结构体。

示例代码 

#include <pybind11/pybind11.h>
namespace py = pybind11;
// 定义一个简单的结构体
struct Point {
    float x, y;
};
// 定义一个带有构造函数的结构体
struct ColoredPoint : Point {
    Color color;  // 使用上面定义的枚举类型作为成员变量
    ColoredPoint(float x, float y, Color color): Point{x, y}, color(color) {}
};
PYBIND11_MODULE(example, m) {
    // 绑定枚举类型
    py::enum_<Color>(m, "Color")
        .value("Red", Color::Red)
        .value("Green", Color::Green)
        .value("Blue", Color::Blue)
        .export_values();
    // 绑定简单结构体
    py::class_<Point>(m, "Point")
        .def(py::init<>())  // 默认构造函数
        .def_readwrite("x", &Point::x)
        .def_readwrite("y", &Point::y);
    // 绑定带有构造函数的结构体
    py::class_<ColoredPoint, Point /* 基类 */>(m, "ColoredPoint")
        .def(py::init<float, float, Color>())  // 自定义构造函数
        .def_readwrite("color", &ColoredPoint::color);
}

在这个例子中:

  • 首先绑定了枚举类型 Color
  • 然后定义了一个简单的结构体 Point,并将其属性 x 和 y 暴露给 Python。
  • 接着定义了一个继承自 Point 的结构体 ColoredPoint,它包含一个额外的 Color 成员,并提供了一个自定义的构造函数。

在 Python 中使用:

import example

# 创建一个 Point 实例
p = example.Point()
p.x = 1.0
p.y = 2.0
print(p.x, p.y)  # 输出: 1.0 2.0

# 创建一个 ColoredPoint 实例
cp = example.ColoredPoint(3.0, 4.0, example.Color.Blue)
print(cp.x, cp.y, cp.color)  # 输出: 3.0 4.0 Color.Blue

封装 C++ 容器类型

示例代码

#include <pybind11/pybind11.h>
#include <pybind11/stl.h> // 包含对 STL 容器的支持
namespace py = pybind11;
// 返回一个 vector<int>
std::vector<int> get_vector() {
    return {1, 2, 3, 4, 5};
}
// 接受一个 vector<int> 参数并返回它
std::vector<int> echo_vector(const std::vector<int>& vec) {
    return vec;
}
// 返回一个 map<string, int>
std::map<std::string, int> get_map() {
    return {{"one", 1}, {"two", 2}, {"three", 3}};
}
// 接受一个 map<string, int> 参数并返回它
std::map<std::string, int> echo_map(const std::map<std::string, int>& m) {
    return m;
}
PYBIND11_MODULE(example, m) {
    m.def("get_vector", &get_vector, "Get a vector of integers");
    m.def("echo_vector", &echo_vector, "Echo a vector of integers");
    m.def("get_map", &get_map, "Get a map of string to integer");
    m.def("echo_map", &echo_map, "Echo a map of string to integer");
}

这里定义了四个函数:

  • get_vector: 返回一个 std::vector<int>。
  • echo_vector: 接收一个 std::vector<int> 参数,并简单地返回它。
  • get_map: 返回一个 std::map<std::string, int>。
  • echo_map: 接收一个 std::map<std::string, int> 参数,并简单地返回它。

【注意】:#include <pybind11/stl.h> 这行代码非常重要,它包含了对 C++ 标准模板库容器的支持。

在 Python 中调用

一旦模块编译完成,就可以像导入任何其他 Python 模块一样导入并使用它:

import example

# 获取并打印一个整数向量
vec = example.get_vector()
print(vec)  # 输出: [1, 2, 3, 4, 5]

# 回显向量
echoed_vec = example.echo_vector([6, 7, 8])
print(echoed_vec)  # 输出: [6, 7, 8]

# 获取并打印一个字符串到整数的映射
mapping = example.get_map()
print(mapping)  # 输出: {'one': 1, 'two': 2, 'three': 3}

# 回显映射
echoed_map = example.echo_map({"four": 4, "five": 5})
print(echoed_map)  # 输出: {'four': 4, 'five': 5}

cv::mat类型

pybind11进行  cv mat 与 numpy.ndarray 之间转换,示例代码

///TyperCaster.h 进行C++ Opencv cv::mat与python numpy.ndarray 之间转换头文件
#include<opencv2/core/core.hpp>
#include<pybind11/pybind11.h>
#include<pybind11/numpy.h>
namespace pybind11 { 
namespace detail{
template<>
struct type_caster<cv::Mat>{
public:   
    PYBIND11_TYPE_CASTER(cv::Mat, _("numpy.ndarray")); 
    //! 1. cast numpy.ndarray to cv::Mat    
    bool load(handle obj, bool){        
        array b = reinterpret_borrow<array>(obj);        
        buffer_info info = b.request();    
        int nh = 1;        
        int nw = 1;        
        int nc = 1;        
        int ndims = info.ndim;        
        if(ndims == 2){           
           nh = info.shape[0];           
           nw = info.shape[1];       
        } 
        else if(ndims == 3){            
            nh = info.shape[0];           
            nw = info.shape[1];           
            nc = info.shape[2];        
        }else{            
            throw std::logic_error("Only support 2d, 2d matrix");            
            return false;       
        }       
        int dtype;        
        if(info.format == format_descriptor<unsigned char>::format()){            
            dtype = CV_8UC(nc);        
        }else if (info.format == format_descriptor<int>::format()){            
            dtype = CV_32SC(nc);       
        }else if (info.format == format_descriptor<float>::format()){           
            dtype = CV_32FC(nc);        
        }else{            
            throw std::logic_error("Unsupported type, only support uchar, int32, float"); 
            return false;
        }   
        value = cv::Mat(nh, nw, dtype, info.ptr);
        return true;    
    }    
    //! 2. cast cv::Mat to numpy.ndarray    
    static handle cast(const cv::Mat& mat, return_value_policy, handle defval){        
        std::string format = format_descriptor<unsigned char>::format();
        size_t elemsize = sizeof(unsigned char);
        int nw = mat.cols;
        int nh = mat.rows;
        int nc = mat.channels();
        int depth = mat.depth();
        int type = mat.type();
        int dim = (depth == type)? 2 : 3;
        if(depth == CV_8U){
            format = format_descriptor<unsigned char>::format();
            elemsize = sizeof(unsigned char);
        }else if(depth == CV_32S){
            format = format_descriptor<int>::format();
            elemsize = sizeof(int);
        }else if(depth == CV_32F){
            format = format_descriptor<float>::format();
            elemsize = sizeof(float);
        }else{            
            throw std::logic_error("Unsupport type, only support uchar, int32, float");
        }        
        std::vector<size_t> bufferdim;
        std::vector<size_t> strides;
        if (dim == 2) {
            bufferdim = {(size_t) nh, (size_t) nw};
            strides = {elemsize * (size_t) nw, elemsize};
        } else if (dim == 3) {
            bufferdim = {(size_t) nh, (size_t) nw, (size_t) nc};
            strides = {(size_t) elemsize * nw * nc, (size_t) elemsize * nc, (size_t) elemsize};
        }
        return array(buffer_info( mat.data,  elemsize,  format, dim, bufferdim, strides )).release();    
}};
}}//! end namespace pybind11::detail

需要使用cv::mat类型数据时,在使用pybind11封装的导出c++库头文件中,包含此TypeCaster.h头文件即可。

智能指针数据类型

需要定义结构体以及相关的操作函数:

#include <pybind11/pybind11.h>
#include <memory> // 包含智能指针
namespace py = pybind11;
// 定义一个简单的结构体
struct Person {
    std::string name;
    int age;
};
// 返回一个包含Person对象的shared_ptr
std::shared_ptr<Person> create_person(const std::string& name, int age) {
    return std::make_shared<Person>(Person{name, age});
}
PYBIND11_MODULE(example, m) {
    // 绑定Person结构体到Python,并指定使用std::shared_ptr进行管理
    py::class_<Person, std::shared_ptr<Person>> cls(m, "Person");
    cls.def(py::init<const std::string&, int>()) // 绑定构造函数
       .def_readwrite("name", &Person::name)
       .def_readwrite("age", &Person::age);
    // 绑定create_person函数,它返回一个Person对象的shared_ptr
    m.def("create_person", &create_person, "Create a new Person instance");
}

在这个例子中:

  • 定义了一个简单的结构体 Person,它有两个成员变量:name 和 age。
  • create_person 函数创建并返回一个 Person 对象的 std::shared_ptr。
  • 在绑定 Person 结构体时,指定了使用 std::shared_ptr<Person> 进行管理,这样 PyBind11 就知道如何自动管理对象的生命周期。

在 Python 中使用

一旦模块编译完成,就可以像导入任何其他 Python 模块一样导入并使用它:

import example

# 创建一个新的 Person 实例
person = example.create_person("Alice", 30)
print(f"Name: {person.name}, Age: {person.age}")  # 输出: Name: Alice, Age: 30

# 修改属性值
person.age = 31
print(f"Updated Age: {person.age}")  # 输出: Updated Age: 31

这个例子展示了如何将一个由 std::shared_ptr 管理的结构体从 C++ 暴露给 Python。通过这种方式,可以利用 C++ 的资源管理和内存安全特性,同时在 Python 中方便地使用这些数据结构。

注意,尽管这里使用的是 std::shared_ptr,PyBind11 同样支持 std::unique_ptr,但通常不建议直接将 std::unique_ptr 返回给 Python,因为它的所有权语义可能会导致复杂性增加。如果确实需要使用 std::unique_ptr,考虑采用工厂模式或其他方法来管理对象的生命周期。

智能指针+结构体+基类

#include <pybind11/pybind11.h>
#include <memory> // 包含智能指针
namespace py = pybind11;
// 定义基类
class Base {
public:
    virtual ~Base() = default; // 虚析构函数确保正确清理派生类对象
    virtual std::string say_hello() const { return "Hello from Base"; }
};
// 定义派生类
struct Derived : public Base {
    std::string name;
    int age;
    Derived(const std::string& name, int age) : name(name), age(age) {}
    std::string say_hello() const override {
        return "Hello from Derived, my name is " + name + " and I'm " + std::to_string(age) + " years old.";
    }
};
// 返回一个包含Derived对象的shared_ptr
std::shared_ptr<Base> create_derived(const std::string& name, int age) {
    return std::make_shared<Derived>(name, age);
}
PYBIND11_MODULE(example, m) {
    // 绑定基类到Python
    py::class_<Base, std::shared_ptr<Base>> base(m, "Base");
    base.def(py::init<>())
         .def("say_hello", &Base::say_hello);
    // 绑定派生类到Python,同时指定它基于哪个基类
    py::class_<Derived, std::shared_ptr<Derived>, Base> derived(m, "Derived");
    derived.def(py::init<const std::string&, int>())
           .def_readwrite("name", &Derived::name)
           .def_readwrite("age", &Derived::age)
           .def("say_hello", &Derived::say_hello);
    // 绑定创建Derived实例的函数
    m.def("create_derived", &create_derived, "Create a new Derived instance");
}

在这个例子中:

  • 定义了一个基类 Base,它有一个虚函数 say_hello()。
  • 定义了一个从 Base 继承的结构体 Derived,它有两个成员变量 name 和 age,并重写了 say_hello() 方法。
  • create_derived 函数创建并返回一个 Derived 对象的 std::shared_ptr<Base>,这允许在 C++ 中使用多态性,同时提供给 Python 使用。
  • 在绑定类时,为 Derived 类指定了它的基类 Base,以及使用的智能指针类型 std::shared_ptr。

在 Python 中使用

一旦模块编译完成,就可以像导入任何其他 Python 模块一样导入并使用它:

import example

# 创建一个新的 Derived 实例
obj = example.create_derived("Alice", 30)
print(obj.say_hello())  # 输出: Hello from Derived, my name is Alice and I'm 30 years old.

# 修改属性值
obj.age = 31
print(f"Updated Age: {obj.age}")  # 输出: Updated Age: 31

# 调用基类的方法
base_obj = example.Base()
print(base_obj.say_hello())  # 输出: Hello from Base

到此这篇关于python调用C++库实现数据类型转换的完整指南的文章就介绍到这了,更多相关python调用C++实现数据类型转换内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Python字符串中删除特定字符的方法

    Python字符串中删除特定字符的方法

    这篇文章主要介绍了Python字符串中删除特定字符的方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-01-01
  • python实现人工蜂群算法

    python实现人工蜂群算法

    这篇文章主要介绍了python如何实现人工蜂群算法,帮助大家更好的利用python进行数据分析,感兴趣的朋友可以了解下
    2020-09-09
  • Python实现带图形界面的炸金花游戏

    Python实现带图形界面的炸金花游戏

    诈金花又叫三张牌,是在全国广泛流传的一种民间多人纸牌游戏,它具有独特的比牌规则。本文将通过Python语言实现带图形界面的诈金花游戏,需要的可以参考一下
    2022-12-12
  • python分割和拼接字符串

    python分割和拼接字符串

    python分割和拼接字符串的实例,使用了string的split和join 方法,并对这二个方法做说明。
    2013-11-11
  • 使用python3调用wxpy模块监控linux日志并定时发送消息给群组或好友

    使用python3调用wxpy模块监控linux日志并定时发送消息给群组或好友

    这篇文章主要介绍了使用python3调用wxpy模块,监控linux日志并定时发送消息给群组或好友,本文通过实例代码给大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友可以参考下
    2019-06-06
  • 使用python读取txt文件的内容,并删除重复的行数方法

    使用python读取txt文件的内容,并删除重复的行数方法

    下面小编就为大家分享一篇使用python读取txt文件的内容,并删除重复的行数方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-04-04
  • pytorch快速搭建神经网络_Sequential操作

    pytorch快速搭建神经网络_Sequential操作

    这篇文章主要介绍了pytorch快速搭建神经网络_Sequential操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-06-06
  • pytorch中的torch.nn.Conv2d()函数图文详解

    pytorch中的torch.nn.Conv2d()函数图文详解

    这篇文章主要给大家介绍了关于pytorch中torch.nn.Conv2d()函数的相关资料,文中通过实例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2022-02-02
  • Python 实现取多维数组第n维的前几位

    Python 实现取多维数组第n维的前几位

    今天小编就为大家分享一篇Python 实现取多维数组第n维的前几位,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2019-11-11
  • Python3.5面向对象程序设计之类的继承和多态详解

    Python3.5面向对象程序设计之类的继承和多态详解

    这篇文章主要介绍了Python3.5面向对象程序设计之类的继承和多态,结合实例形式详细分析了Python3.5面向对象程序设计中类的继承与多态常见用法及相关注意事项,需要的朋友可以参考下
    2019-04-04

最新评论