python調(diào)用C++庫(kù)實(shí)現(xiàn)數(shù)據(jù)類(lèi)型轉(zhuǎn)換的完整指南
枚舉類(lèi)型的封裝
假設(shè)有一個(gè)簡(jiǎn)單的枚舉類(lèi)型 Color,可以直接將其暴露給 Python。
示例代碼
#include <pybind11/pybind11.h>
namespace py = pybind11;
// 定義一個(gè)枚舉類(lèi)型
enum class Color { Red, Green, Blue };
PYBIND11_MODULE(example, m) {
// 綁定枚舉類(lèi)型到 Python
py::enum_<Color>(m, "Color")
.value("Red", Color::Red)
.value("Green", Color::Green)
.value("Blue", Color::Blue)
.export_values(); // 使枚舉值可以直接通過(guò)模塊訪問(wèn)
}在 Python 中使用:
import example print(example.Color.Red) # 輸出: Color.Red print(int(example.Color.Green)) # 輸出: 1
結(jié)構(gòu)體的封裝
接下來(lái),看一個(gè)稍微復(fù)雜的例子,包括如何封裝結(jié)構(gòu)體和帶有構(gòu)造函數(shù)的結(jié)構(gòu)體。
示例代碼
#include <pybind11/pybind11.h>
namespace py = pybind11;
// 定義一個(gè)簡(jiǎn)單的結(jié)構(gòu)體
struct Point {
float x, y;
};
// 定義一個(gè)帶有構(gòu)造函數(shù)的結(jié)構(gòu)體
struct ColoredPoint : Point {
Color color; // 使用上面定義的枚舉類(lèi)型作為成員變量
ColoredPoint(float x, float y, Color color): Point{x, y}, color(color) {}
};
PYBIND11_MODULE(example, m) {
// 綁定枚舉類(lèi)型
py::enum_<Color>(m, "Color")
.value("Red", Color::Red)
.value("Green", Color::Green)
.value("Blue", Color::Blue)
.export_values();
// 綁定簡(jiǎn)單結(jié)構(gòu)體
py::class_<Point>(m, "Point")
.def(py::init<>()) // 默認(rèn)構(gòu)造函數(shù)
.def_readwrite("x", &Point::x)
.def_readwrite("y", &Point::y);
// 綁定帶有構(gòu)造函數(shù)的結(jié)構(gòu)體
py::class_<ColoredPoint, Point /* 基類(lèi) */>(m, "ColoredPoint")
.def(py::init<float, float, Color>()) // 自定義構(gòu)造函數(shù)
.def_readwrite("color", &ColoredPoint::color);
}在這個(gè)例子中:
- 首先綁定了枚舉類(lèi)型
Color。 - 然后定義了一個(gè)簡(jiǎn)單的結(jié)構(gòu)體
Point,并將其屬性x和y暴露給 Python。 - 接著定義了一個(gè)繼承自
Point的結(jié)構(gòu)體ColoredPoint,它包含一個(gè)額外的Color成員,并提供了一個(gè)自定義的構(gòu)造函數(shù)。
在 Python 中使用:
import example # 創(chuàng)建一個(gè) Point 實(shí)例 p = example.Point() p.x = 1.0 p.y = 2.0 print(p.x, p.y) # 輸出: 1.0 2.0 # 創(chuàng)建一個(gè) ColoredPoint 實(shí)例 cp = example.ColoredPoint(3.0, 4.0, example.Color.Blue) print(cp.x, cp.y, cp.color) # 輸出: 3.0 4.0 Color.Blue
封裝 C++ 容器類(lèi)型
示例代碼
#include <pybind11/pybind11.h>
#include <pybind11/stl.h> // 包含對(duì) STL 容器的支持
namespace py = pybind11;
// 返回一個(gè) vector<int>
std::vector<int> get_vector() {
return {1, 2, 3, 4, 5};
}
// 接受一個(gè) vector<int> 參數(shù)并返回它
std::vector<int> echo_vector(const std::vector<int>& vec) {
return vec;
}
// 返回一個(gè) map<string, int>
std::map<std::string, int> get_map() {
return {{"one", 1}, {"two", 2}, {"three", 3}};
}
// 接受一個(gè) map<string, int> 參數(shù)并返回它
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");
}這里定義了四個(gè)函數(shù):
- get_vector: 返回一個(gè) std::vector<int>。
- echo_vector: 接收一個(gè) std::vector<int> 參數(shù),并簡(jiǎn)單地返回它。
- get_map: 返回一個(gè) std::map<std::string, int>。
- echo_map: 接收一個(gè) std::map<std::string, int> 參數(shù),并簡(jiǎn)單地返回它。
【注意】:#include <pybind11/stl.h> 這行代碼非常重要,它包含了對(duì) C++ 標(biāo)準(zhǔn)模板庫(kù)容器的支持。
在 Python 中調(diào)用
一旦模塊編譯完成,就可以像導(dǎo)入任何其他 Python 模塊一樣導(dǎo)入并使用它:
import example
# 獲取并打印一個(gè)整數(shù)向量
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]
# 獲取并打印一個(gè)字符串到整數(shù)的映射
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類(lèi)型
pybind11進(jìn)行 cv mat 與 numpy.ndarray 之間轉(zhuǎn)換,示例代碼
///TyperCaster.h 進(jìn)行C++ Opencv cv::mat與python numpy.ndarray 之間轉(zhuǎn)換頭文件
#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類(lèi)型數(shù)據(jù)時(shí),在使用pybind11封裝的導(dǎo)出c++庫(kù)頭文件中,包含此TypeCaster.h頭文件即可。
智能指針數(shù)據(jù)類(lèi)型
需要定義結(jié)構(gòu)體以及相關(guān)的操作函數(shù):
#include <pybind11/pybind11.h>
#include <memory> // 包含智能指針
namespace py = pybind11;
// 定義一個(gè)簡(jiǎn)單的結(jié)構(gòu)體
struct Person {
std::string name;
int age;
};
// 返回一個(gè)包含Person對(duì)象的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結(jié)構(gòu)體到Python,并指定使用std::shared_ptr進(jìn)行管理
py::class_<Person, std::shared_ptr<Person>> cls(m, "Person");
cls.def(py::init<const std::string&, int>()) // 綁定構(gòu)造函數(shù)
.def_readwrite("name", &Person::name)
.def_readwrite("age", &Person::age);
// 綁定create_person函數(shù),它返回一個(gè)Person對(duì)象的shared_ptr
m.def("create_person", &create_person, "Create a new Person instance");
}在這個(gè)例子中:
- 定義了一個(gè)簡(jiǎn)單的結(jié)構(gòu)體 Person,它有兩個(gè)成員變量:name 和 age。
- create_person 函數(shù)創(chuàng)建并返回一個(gè) Person 對(duì)象的 std::shared_ptr。
- 在綁定 Person 結(jié)構(gòu)體時(shí),指定了使用 std::shared_ptr<Person> 進(jìn)行管理,這樣 PyBind11 就知道如何自動(dòng)管理對(duì)象的生命周期。
在 Python 中使用
一旦模塊編譯完成,就可以像導(dǎo)入任何其他 Python 模塊一樣導(dǎo)入并使用它:
import example
# 創(chuàng)建一個(gè)新的 Person 實(shí)例
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這個(gè)例子展示了如何將一個(gè)由 std::shared_ptr 管理的結(jié)構(gòu)體從 C++ 暴露給 Python。通過(guò)這種方式,可以利用 C++ 的資源管理和內(nèi)存安全特性,同時(shí)在 Python 中方便地使用這些數(shù)據(jù)結(jié)構(gòu)。
注意,盡管這里使用的是 std::shared_ptr,PyBind11 同樣支持 std::unique_ptr,但通常不建議直接將 std::unique_ptr 返回給 Python,因?yàn)樗乃袡?quán)語(yǔ)義可能會(huì)導(dǎo)致復(fù)雜性增加。如果確實(shí)需要使用 std::unique_ptr,考慮采用工廠模式或其他方法來(lái)管理對(duì)象的生命周期。
智能指針+結(jié)構(gòu)體+基類(lèi)
#include <pybind11/pybind11.h>
#include <memory> // 包含智能指針
namespace py = pybind11;
// 定義基類(lèi)
class Base {
public:
virtual ~Base() = default; // 虛析構(gòu)函數(shù)確保正確清理派生類(lèi)對(duì)象
virtual std::string say_hello() const { return "Hello from Base"; }
};
// 定義派生類(lèi)
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.";
}
};
// 返回一個(gè)包含Derived對(duì)象的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) {
// 綁定基類(lèi)到Python
py::class_<Base, std::shared_ptr<Base>> base(m, "Base");
base.def(py::init<>())
.def("say_hello", &Base::say_hello);
// 綁定派生類(lèi)到Python,同時(shí)指定它基于哪個(gè)基類(lèi)
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);
// 綁定創(chuàng)建Derived實(shí)例的函數(shù)
m.def("create_derived", &create_derived, "Create a new Derived instance");
}在這個(gè)例子中:
- 定義了一個(gè)基類(lèi) Base,它有一個(gè)虛函數(shù) say_hello()。
- 定義了一個(gè)從 Base 繼承的結(jié)構(gòu)體 Derived,它有兩個(gè)成員變量 name 和 age,并重寫(xiě)了 say_hello() 方法。
- create_derived 函數(shù)創(chuàng)建并返回一個(gè) Derived 對(duì)象的 std::shared_ptr<Base>,這允許在 C++ 中使用多態(tài)性,同時(shí)提供給 Python 使用。
- 在綁定類(lèi)時(shí),為 Derived 類(lèi)指定了它的基類(lèi) Base,以及使用的智能指針類(lèi)型 std::shared_ptr。
在 Python 中使用
一旦模塊編譯完成,就可以像導(dǎo)入任何其他 Python 模塊一樣導(dǎo)入并使用它:
import example
# 創(chuàng)建一個(gè)新的 Derived 實(shí)例
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
# 調(diào)用基類(lèi)的方法
base_obj = example.Base()
print(base_obj.say_hello()) # 輸出: Hello from Base到此這篇關(guān)于python調(diào)用C++庫(kù)實(shí)現(xiàn)數(shù)據(jù)類(lèi)型轉(zhuǎn)換的完整指南的文章就介紹到這了,更多相關(guān)python調(diào)用C++實(shí)現(xiàn)數(shù)據(jù)類(lèi)型轉(zhuǎn)換內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
matplotlib交互式數(shù)據(jù)光標(biāo)mpldatacursor的實(shí)現(xiàn)
這篇文章主要介紹了matplotlib交互式數(shù)據(jù)光標(biāo)mpldatacursor的實(shí)現(xiàn) ,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-02-02
python使用websocket庫(kù)發(fā)送WSS請(qǐng)求
WebSocket是一種在客戶端和服務(wù)器之間進(jìn)行雙向通信的協(xié)議,Python中有許多WebSocket庫(kù)可供選擇,其中一個(gè)常用的是websocket庫(kù),使用該庫(kù)可以輕松地發(fā)送WSS請(qǐng)求,需要的朋友可以參考下2023-10-10
Python處理文件寫(xiě)入時(shí)文件不存在的完整解決方案
在現(xiàn)代軟件開(kāi)發(fā)中,安全地處理文件操作是每個(gè)開(kāi)發(fā)者必須掌握的核心技能,本文主要為大家詳細(xì)介紹了Python處理文件寫(xiě)入時(shí)文件不存在相關(guān)解決方法,有需要的小伙伴可以了解下2025-09-09
pd.DataFrame統(tǒng)計(jì)各列數(shù)值多少的實(shí)例
今天小編就為大家分享一篇pd.DataFrame統(tǒng)計(jì)各列數(shù)值多少的實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2019-12-12
Python網(wǎng)絡(luò)編程之HTTP協(xié)議的python應(yīng)用
HTTP是在網(wǎng)絡(luò)上傳輸HTML的協(xié)議,用于瀏覽器和服務(wù)器的通信,這篇文章主要介紹了Python網(wǎng)絡(luò)編程之HTTP協(xié)議的python應(yīng)用,需要的朋友可以參考下2022-11-11
python多進(jìn)程實(shí)現(xiàn)數(shù)據(jù)共享的示例代碼
本文介紹了Python中多進(jìn)程實(shí)現(xiàn)數(shù)據(jù)共享的方法,包括使用multiprocessing模塊和manager模塊這兩種方法,具有一定的參考價(jià)值,感興趣的可以了解一下2025-01-01
Python字符串的創(chuàng)建和駐留機(jī)制詳解
字符串駐留是一種在內(nèi)存中僅保存一份相同且不可變字符串的方法,本文重點(diǎn)給大家介紹Python字符串的創(chuàng)建和駐留機(jī)制,感興趣的朋友跟隨小編一起看看吧2022-02-02
基于Opencv圖像識(shí)別實(shí)現(xiàn)答題卡識(shí)別示例詳解
這篇文章主要為大家詳細(xì)介紹了基于OpenCV如何實(shí)現(xiàn)答題卡識(shí)別,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-12-12

