当前位置: 首页 > news >正文

使用Pybind11,Python调用C++动态库

最近学习了一下pybind11,使用python来调用C++动态库的模式,在某些场景下有用,这里做一个记录。

环境准备

安装python,我这里安装的是3.12版本

下载Pybind11库,这是一个仅包含头文件的轻量级库,使用起来非常方便。可以在Github下载其Release版本 pybind11

可以参考:官方文档

C++我这边使用的是VS2022,Windows环境

工程配置

首先创建C++工程,这里需要创建的是一个动态库的工程。现在创建动态库工程会生成预编译头,我这里把它给关闭了(在项目属性中选到C/C++—预编译头,然后预编译头那一栏选则为不使用预编译头),删除项目自动创建的一些源文件,创建自己需要的cpp文件即可,我这里创建的是 Pybind11Test.cpp。

工程创建完成之后,在解决方案管理器页面,右键工程打开属性页:

        选中高级,然后修改右侧的目标文件扩展名为 .pyd

        选中VC++目录,在右侧包含目录中添加Python头文件位置和Pybind11头文件位置,再在库目录中添加Python的库目录

        然后选中连接器—输入,在附加依赖项中添加python的库文件,我这里添加的是python312.lib,这里注意python还有一个Debug版的库,python312_d.lib,我这里本是Debug版运行的,想着应该引用python312_d.lib版本的库,结果在运行时候会抛出异常,后面换成不带_d的版本就好了。

        选中调试项,在右侧命令项输入Python.exe的全路径地址,然后命令参数输入测试的python文件名,如Test.py,在工作目录项填入工程Debug目录的生成文件位置,记得在此路径下创建python的测试文件(Test.py)

到此工程环境就配置好了,我们在使用时仅需包含pybind11的头文件即可:

#include <pybind11/pybind11.h>
namespace py = pybind11;

这里简化了一下Pybind11的命名空间。        

导出函数与属性

先做一个简单测试,从C++中导出函数与属性供python使用,我们在C++中定义几个简单函数并导出,同时导出几个模块属性:

C++代码:

int add(int i, int j)
{return i + j;
}
int multiply(int i, int j)
{return i * j;
}
int divide(int i, int j)
{return i / j;
}PYBIND11_MODULE(Pybind11Test, m)
{m.doc() = "pybind11 Pybind11Test plugin";                   //文档描述m.attr("the_answer") = 42;                                  //定义属性py::object world = py::cast("World");m.attr("what") = world;m.def("add", &add, "A function that adds two numbers");         //定义普通函数m.def("multiply", &multiply, py::arg("i"), py::arg("j"));       //定义带命名参数的函数using namespace pybind11::literals;m.def("divide", &divide, "i"_a, "j"_a);                         //简写的带命名参数的函数m.def("divide2", &divide, "i"_a = 20, "j"_a = 2);               //提供默认值的函数m.def("divide3", &divide, py::arg("i") = 30, py::arg("j") = 2); //提供默认值的函数
}

python代码:

import Pybind11Testdef main():print("the_answer:%s" % Pybind11Test.the_answer)print("what:%s" % Pybind11Test.what)print("add:%d" % Pybind11Test.add(1, 2))print("multiply:%d" % Pybind11Test.multiply(1, 2))print("divide:%d" % Pybind11Test.divide(j=10, i=2))print("divide2:%d" % Pybind11Test.divide2())print("divide3:%d" % Pybind11Test.divide3())if __name__ == "__main__":help(Pybind11Test)main()

输出:

PS C:\WorkSpace\C++\Pybind11Test\x64\Debug> python .\Test.py
Help on module Pybind11Test:NAMEPybind11Test - pybind11 Pybind11Test pluginFUNCTIONSadd(...) method of builtins.PyCapsule instanceadd(arg0: int, arg1: int) -> intA function that adds two numbersdivide(...) method of builtins.PyCapsule instancedivide(i: int, j: int) -> intdivide2(...) method of builtins.PyCapsule instancedivide2(i: int = 20, j: int = 2) -> intdivide3(...) method of builtins.PyCapsule instancedivide3(i: int = 30, j: int = 2) -> intmultiply(...) method of builtins.PyCapsule instancemultiply(i: int, j: int) -> intDATAthe_answer = 42what = 'World'FILEc:\workspace\c++\pybind11test\x64\debug\pybind11test.pydthe_answer:42
what:World
add:3
multiply:2
divide:0
divide2:10
divide3:15

这里首先调用了help来查看文档,当定义的接口函数多了之后,这个文档会比较长。这里测试定义了两个模块属性,以及几个全局函数,并有命名参数函数和带默认值的函数。

类与派生类的导出

接下来我们来看看如何从C++中导出类,在C++中定义了两个类,Pet与Dog,Dog继承自Pet

class Pet
{
public:Pet(const std::string& name, int age) : Name(name), Age(age) { }void setName(const std::string& name) { Name = name; }const std::string& getName() const { return Name; }void setAge(int age) { Age = age; }int getAge() const { return Age; }
public:std::string Name;
private:int Age;
};class Dog : public Pet
{
public:Dog(const std::string& name, int age) : Pet(name, age){ }std::string bark() const { return "woof!"; }
};PYBIND11_MODULE(Pybind11Test, m)
{m.doc() = "pybind11 Pybind11Test plugin";                   //文档描述py::class_<Pet>(m, "Pet", py::dynamic_attr())               //定义类.def(py::init<const std::string&, int>())               //构造函数.def("setName", &Pet::setName)                          //成员函数.def("getName", &Pet::getName)                          //成员函数.def_readonly("name", &Pet::Name)                       //只读属性.def_readwrite("Name", &Pet::Name)                      //读写属性.def_property("Age", &Pet::getAge, &Pet::setAge);       //定义属性,提供getter和setterpy::class_<Dog, Pet>(m, "Dog")                              //定义派生类,此继承是非多态的.def(py::init<const std::string&, int>())               //构造函数.def("bark", &Dog::bark);                               //成员函数m.def("getPet", []() {return std::unique_ptr<Pet>(new Dog("Kitty", 0)); }); //getPet实际返回的是Dog类型,但是因为此继承是非多态的,在调用Dog的bark方法是会抛出异常py::class_<PolymorphicPet>(m, "PolymorphicPet")             //PolymorphicPet包含有虚函数,pybind11会识别到,继承于PolymorphicPet的类是多态的.def(py::init<>());py::class_<PolymorphicDog, PolymorphicPet>(m, "PolymorphicDog").def(py::init<>()).def("bark", &PolymorphicDog::bark);m.def("getPolymorphicPet", []() {return std::unique_ptr<PolymorphicPet>(new PolymorphicDog()); }); //getPolymorphicPet实际返回的是PolymorphicDog类型,pybind11会自动转换为派生类类型,并可以访问派生类独有的成员函数//请注意,这超出了C++中通常的情况:我们不仅可以访问基类的虚函数,还可以获得具体的派生类型,包括基类类型甚至可能不知道的函数和属性。//类似于C++中对声明为基类类型的指针(实际上是派生类指针)进行强转为派生类指针
}

python代码:

import Pybind11Testdef main():p = Pybind11Test.Pet("Molly", 20)print(p.getName())p.setName("Charly")print(p.getName())p.Name = "Lucy"print(p.name)print(p.Name)print(p.Age)p.Age = 25print(p.Age)p.Location = "China"print(p.Location)print(p.__dict__)d = Pybind11Test.Dog("Wangcai", 2)d.Location = "Shanghai"print(d.name)print(d.Name)print(d.Age)print(d.bark())print(d.Location)p2 = Pybind11Test.getPet()print(p2.Name)print(p2.Age)#print(p2.bark())   #AttributeError: 'Pybind11Test.Pet' object has no attribute 'bark'p3 = Pybind11Test.getPolymorphicPet()print("p3:%s" % p3.bark())if __name__ == "__main__":#help(Pybind11Test)main()

输出:

PS C:\WorkSpace\C++\Pybind11Test\x64\Debug> python .\Test.py
Molly
Charly
Lucy
Lucy
20
25
China
{'Location': 'China'}
Wangcai
Wangcai
2
woof!
Shanghai
Kitty
0
p3:woof!

如果基类没有虚函数,那么继承于基类的子类不具有多态性,如上面示例:Dog继承于Pet,getPet函数返回声明的是一个Pet的指针,但是实际提供的是一个Dog对象的指针,在python中使用这个返回值调用Dog专有的函数时会发生异常。而PolymorphicPet 包含一个虚函数,PolymorphicDog继承于PolymorphicPet,同样,getPolymorphicPet 返回类型声明的是PolymorphicPet 类型指针,实际返回的是 PolymorphicDog 类型指针,Pybind11 会识别到实际返回的类型,并将其提供给python侧,python那边获取到的返回值类型实际就是 PolymorphicDog。

重载函数的导出

C++中常常有函数重载的情况,但是python中没有函数重载,我们这里可以把重载的函数都绑定到同一个函数名导出也可以绑定到不同的函数名导出,如何两个重载的成员函数的参数类型相同,仅有const的区别,则需要为两者导出为不同的函数名。

C++代码:

int Square(int a)
{return a * a;
}
double Square(double a)
{return a * a;
}
std::string Square(const std::string& a)
{return a + a;
}
class OverloadClass
{
public:OverloadClass(){}int Square(int a){return a * a;}int Square(int a) const{return a + a;}double Square(double a){return a * a;}std::string Square(const std::string& a){return a + a;}
};PYBIND11_MODULE(Pybind11Test, m)
{m.doc() = "pybind11 Pybind11Test plugin";                   //文档描述m.def("Square", static_cast<int (*)(int)>(&Square));                            //函数重载m.def("Square", static_cast<double (*)(double)>(&Square));                      //函数重载m.def("Square", static_cast<std::string (*)(const std::string&)>(&Square));     //函数重载m.def("Square2", py::overload_cast<int>(&Square));                              //简化版函数重载m.def("Square2", py::overload_cast<double>(&Square));                           //简化版函数重载m.def("Square2", py::overload_cast<const std::string&>(&Square));               //简化版函数重载py::class_<OverloadClass>(m, "OverloadClass").def(py::init<>()).def("Square", static_cast<int (OverloadClass::*)(int)>(&OverloadClass::Square))                           //成员函数重载.def("Square", static_cast<double (OverloadClass::*)(double)>(&OverloadClass::Square))                     //成员函数重载.def("Square", static_cast<std::string(OverloadClass::*)(const std::string&)>(&OverloadClass::Square))     //成员函数重载.def("Square2", py::overload_cast<int>(&OverloadClass::Square))                                            //简化版成员函数重载.def("Square2", py::overload_cast<double>(&OverloadClass::Square))                                         //简化版成员函数重载.def("Square2", py::overload_cast<const std::string&>(&OverloadClass::Square))                             //简化版成员函数重载.def("Square2Const", py::overload_cast<int>(&OverloadClass::Square, py::const_));                          //简化版成员函数重载,const版本,这里需要使用不同的名称用以区分
}

python代码:

import Pybind11Testdef main():print("Square(3):%s" % Pybind11Test.Square(3))print("Square(3.1):%s" % Pybind11Test.Square(3.1))print("Square('test'):%s" % Pybind11Test.Square('test'))print("Square2(3):%s" % Pybind11Test.Square2(3))print("Square2(3.1):%s" % Pybind11Test.Square2(3.1))print("Square2('test'):%s" % Pybind11Test.Square2('test'))s = Pybind11Test.OverloadClass()print("OverloadClass.Square(3):%s" % s.Square(3))print("OverloadClass.Square(3.1):%s" % s.Square(3.1))print("OverloadClass.Square('test'):%s" % s.Square('test'))print("OverloadClass.Square2(3):%s" % s.Square2(3))print("OverloadClass.Square2(3.1):%s" % s.Square2(3.1))print("OverloadClass.Square2('test'):%s" % s.Square2('test'))print("OverloadClass.Square2Const('test'):%s" % s.Square2Const(5))returnif __name__ == "__main__":#help(Pybind11Test)main()

输出:

PS C:\WorkSpace\C++\Pybind11Test\x64\Debug> python .\Test.py
Square(3):9
Square(3.1):9.610000000000001
Square('test'):testtest
Square2(3):9
Square2(3.1):9.610000000000001
Square2('test'):testtest
OverloadClass.Square(3):9
OverloadClass.Square(3.1):9.610000000000001
OverloadClass.Square('test'):testtest
OverloadClass.Square2(3):9
OverloadClass.Square2(3.1):9.610000000000001
OverloadClass.Square2('test'):testtest
OverloadClass.Square2Const('test'):10

枚举类型与内部类的导出

在C++类内部创建的枚举类型、类类型亦可导出,关于枚举类型,还可以使用 export_values函数将其枚举值导出到父范围,对于新的C++11风格的强类型枚举类型,不要使用此函数。

C++代码:

class Animal
{
public:enum Kind{Dog = 0,Cat = 2};enum class Gender{Male = 0,Female = 1};class Attributes{public:int Age = 0;};Animal(std::string name, Kind type, Gender sex):Name(name), Type(type), Sex(sex){}std::string Name;Kind Type;Gender Sex;Attributes Attr;
};PYBIND11_MODULE(Pybind11Test, m)
{m.doc() = "pybind11 Pybind11Test plugin";                   //文档描述py::class_<Animal> animal(m, "Animal");             //为了确保在Animal范围内创建嵌套类型,需要将Animal的class_实例传给其内部类型的构造函数,所以在此位置声明一个Animal类型的实例 animalanimal.def(py::init<const std::string&, Animal::Kind, Animal::Gender>()).def_readwrite("Name", &Animal::Name).def_readwrite("Type", &Animal::Type).def_readwrite("Sex", &Animal::Sex).def_readwrite("Attr", &Animal::Attr);py::enum_<Animal::Kind>(animal, "Kind")             //接受animal实例作为参数,标识Kind为Animal的内部枚举类型.value("Dog", Animal::Kind::Dog).value("Cat", Animal::Kind::Cat).export_values();                               //export_values 将枚举项导出到父范围,在python中可以使用 Animal.Cat,对于新的C++11风格的强类型枚举,不要使用此操作py::enum_<Animal::Gender>(animal, "Gender")         //接受animal实例作为参数,标识Gender为Animal的内部枚举类型.value("Male", Animal::Gender::Male).value("Female", Animal::Gender::Female);py::class_<Animal::Attributes>(animal, "Attributes")    //接受animal实例作为参数,标识Attributes为Animal的内部类类型.def(py::init<>()).def_readwrite("Age", &Animal::Attributes::Age);
}

python代码:

import Pybind11Testdef main():animal = Pybind11Test.Animal("Garfield", Pybind11Test.Animal.Cat, Pybind11Test.Animal.Gender.Male)animal.Attr.Age = 15print("Type:%s, int Type:%s" % (animal.Type, int(animal.Type)))print("Sex:%s, int Sex:%s" % (animal.Sex, int(animal.Sex)))print("Age:%s" % animal.Attr.Age)print(Pybind11Test.Animal.Kind.__members__)print(Pybind11Test.Animal.Gender.__members__)print("运行完毕,请按回车键退出。")input()if __name__ == "__main__":#help(Pybind11Test)main()

输出:

PS C:\WorkSpace\C++\Pybind11Test\x64\Debug> python .\Test.py
Type:Kind.Cat, int Type:2
Sex:Gender.Male, int Sex:0
Age:15
{'Dog': <Kind.Dog: 0>, 'Cat': <Kind.Cat: 2>}
{'Male': <Gender.Male: 0>, 'Female': <Gender.Female: 1>}
运行完毕,请按回车键退出。

整体测试下来,从python调用C++的动态库还是比较方便的,目前主要是根据官方文档逐个测试下来,还有一些更复杂的功能点目前没有测试,留待以后再试。在这里贴一下整体的测试代码:

C++代码:

// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include <pybind11/pybind11.h>namespace py = pybind11;int add(int i, int j)
{return i + j;
}
int multiply(int i, int j)
{return i * j;
}
int divide(int i, int j)
{return i / j;
}class Pet
{
public:Pet(const std::string& name, int age) : Name(name), Age(age) { }void setName(const std::string& name) { Name = name; }const std::string& getName() const { return Name; }void setAge(int age) { Age = age; }int getAge() const { return Age; }
public:std::string Name;
private:int Age;
};class Dog : public Pet
{
public:Dog(const std::string& name, int age) : Pet(name, age){ }std::string bark() const { return "woof!"; }
};class PolymorphicPet
{
public:virtual ~PolymorphicPet() = default;
};class PolymorphicDog : public PolymorphicPet
{
public:std::string bark() const { return "woof!"; }
};int Square(int a)
{return a * a;
}
double Square(double a)
{return a * a;
}
std::string Square(const std::string& a)
{return a + a;
}
class OverloadClass
{
public:OverloadClass(){}int Square(int a){return a * a;}int Square(int a) const{return a + a;}double Square(double a){return a * a;}std::string Square(const std::string& a){return a + a;}
};class Animal
{
public:enum Kind{Dog = 0,Cat = 2};enum class Gender{Male = 0,Female = 1};class Attributes{public:int Age = 0;};Animal(std::string name, Kind type, Gender sex):Name(name), Type(type), Sex(sex){}std::string Name;Kind Type;Gender Sex;Attributes Attr;
};PYBIND11_MODULE(Pybind11Test, m)
{m.doc() = "pybind11 Pybind11Test plugin";                   //文档描述m.attr("the_answer") = 42;                                  //定义属性py::object world = py::cast("World");m.attr("what") = world;m.def("add", &add, "A function that adds two numbers");         //定义普通函数m.def("multiply", &multiply, py::arg("i"), py::arg("j"));       //定义带命名参数的函数using namespace pybind11::literals;m.def("divide", &divide, "i"_a, "j"_a);                         //简写的带命名参数的函数m.def("divide2", &divide, "i"_a = 20, "j"_a = 2);               //提供默认值的函数m.def("divide3", &divide, py::arg("i") = 30, py::arg("j") = 2); //提供默认值的函数py::class_<Pet>(m, "Pet", py::dynamic_attr())               //定义类.def(py::init<const std::string&, int>())               //构造函数.def("setName", &Pet::setName)                          //成员函数.def("getName", &Pet::getName)                          //成员函数.def_readonly("name", &Pet::Name)                       //只读属性.def_readwrite("Name", &Pet::Name)                      //读写属性.def_property("Age", &Pet::getAge, &Pet::setAge);       //定义属性,提供getter和setterpy::class_<Dog, Pet>(m, "Dog")                              //定义派生类,此继承是非多态的.def(py::init<const std::string&, int>())               //构造函数.def("bark", &Dog::bark);                               //成员函数m.def("getPet", []() {return std::unique_ptr<Pet>(new Dog("Kitty", 0)); }); //getPet实际返回的是Dog类型,但是因为此继承是非多态的,在调用Dog的bark方法是会抛出异常py::class_<PolymorphicPet>(m, "PolymorphicPet")             //PolymorphicPet包含有虚函数,pybind11会识别到,继承于PolymorphicPet的类是多态的.def(py::init<>());py::class_<PolymorphicDog, PolymorphicPet>(m, "PolymorphicDog").def(py::init<>()).def("bark", &PolymorphicDog::bark);m.def("getPolymorphicPet", []() {return std::unique_ptr<PolymorphicPet>(new PolymorphicDog()); }); //getPolymorphicPet实际返回的是PolymorphicDog类型,pybind11会自动转换为派生类类型,并可以访问派生类独有的成员函数//请注意,这超出了C++中通常的情况:我们不仅可以访问基类的虚函数,还可以获得具体的派生类型,包括基类类型甚至可能不知道的函数和属性。//类似于C++中对声明为基类类型的指针(实际上是派生类指针)进行强转为派生类指针m.def("Square", static_cast<int (*)(int)>(&Square));                            //函数重载m.def("Square", static_cast<double (*)(double)>(&Square));                      //函数重载m.def("Square", static_cast<std::string (*)(const std::string&)>(&Square));     //函数重载m.def("Square2", py::overload_cast<int>(&Square));                              //简化版函数重载m.def("Square2", py::overload_cast<double>(&Square));                           //简化版函数重载m.def("Square2", py::overload_cast<const std::string&>(&Square));               //简化版函数重载py::class_<OverloadClass>(m, "OverloadClass").def(py::init<>()).def("Square", static_cast<int (OverloadClass::*)(int)>(&OverloadClass::Square))                           //成员函数重载.def("Square", static_cast<double (OverloadClass::*)(double)>(&OverloadClass::Square))                     //成员函数重载.def("Square", static_cast<std::string(OverloadClass::*)(const std::string&)>(&OverloadClass::Square))     //成员函数重载.def("Square2", py::overload_cast<int>(&OverloadClass::Square))                                            //简化版成员函数重载.def("Square2", py::overload_cast<double>(&OverloadClass::Square))                                         //简化版成员函数重载.def("Square2", py::overload_cast<const std::string&>(&OverloadClass::Square))                             //简化版成员函数重载.def("Square2Const", py::overload_cast<int>(&OverloadClass::Square, py::const_));                          //简化版成员函数重载,const版本,这里需要使用不同的名称用以区分py::class_<Animal> animal(m, "Animal");             //为了确保在Animal范围内创建嵌套类型,需要将Animal的class_实例传给其内部类型的构造函数,所以在此位置声明一个Animal类型的实例 animalanimal.def(py::init<const std::string&, Animal::Kind, Animal::Gender>()).def_readwrite("Name", &Animal::Name).def_readwrite("Type", &Animal::Type).def_readwrite("Sex", &Animal::Sex).def_readwrite("Attr", &Animal::Attr);py::enum_<Animal::Kind>(animal, "Kind")             //接受animal实例作为参数,标识Kind为Animal的内部枚举类型.value("Dog", Animal::Kind::Dog).value("Cat", Animal::Kind::Cat).export_values();                               //export_values 将枚举项导出到父范围,在python中可以使用 Animal.Cat,对于新的C++11风格的强类型枚举,不要使用此操作py::enum_<Animal::Gender>(animal, "Gender")         //接受animal实例作为参数,标识Gender为Animal的内部枚举类型.value("Male", Animal::Gender::Male).value("Female", Animal::Gender::Female);py::class_<Animal::Attributes>(animal, "Attributes")    //接受animal实例作为参数,标识Attributes为Animal的内部类类型.def(py::init<>()).def_readwrite("Age", &Animal::Attributes::Age);
}

python代码:

import Pybind11Testdef main():print("the_answer:%s" % Pybind11Test.the_answer)print("what:%s" % Pybind11Test.what)print("add:%d" % Pybind11Test.add(1, 2))print("multiply:%d" % Pybind11Test.multiply(1, 2))print("divide:%d" % Pybind11Test.divide(j=10, i=2))print("divide2:%d" % Pybind11Test.divide2())print("divide3:%d" % Pybind11Test.divide3())p = Pybind11Test.Pet("Molly", 20)print(p.getName())p.setName("Charly")print(p.getName())p.Name = "Lucy"print(p.name)print(p.Name)print(p.Age)p.Age = 25print(p.Age)p.Location = "China"print(p.Location)print(p.__dict__)d = Pybind11Test.Dog("Wangcai", 2)d.Location = "Shanghai"print(d.name)print(d.Name)print(d.Age)print(d.bark())print(d.Location)p2 = Pybind11Test.getPet()print(p2.Name)print(p2.Age)#print(p2.bark())   #AttributeError: 'Pybind11Test.Pet' object has no attribute 'bark'p3 = Pybind11Test.getPolymorphicPet()print("p3:%s" % p3.bark())print("Square(3):%s" % Pybind11Test.Square(3))print("Square(3.1):%s" % Pybind11Test.Square(3.1))print("Square('test'):%s" % Pybind11Test.Square('test'))print("Square2(3):%s" % Pybind11Test.Square2(3))print("Square2(3.1):%s" % Pybind11Test.Square2(3.1))print("Square2('test'):%s" % Pybind11Test.Square2('test'))s = Pybind11Test.OverloadClass()print("OverloadClass.Square(3):%s" % s.Square(3))print("OverloadClass.Square(3.1):%s" % s.Square(3.1))print("OverloadClass.Square('test'):%s" % s.Square('test'))print("OverloadClass.Square2(3):%s" % s.Square2(3))print("OverloadClass.Square2(3.1):%s" % s.Square2(3.1))print("OverloadClass.Square2('test'):%s" % s.Square2('test'))print("OverloadClass.Square2Const('test'):%s" % s.Square2Const(5))animal = Pybind11Test.Animal("Garfield", Pybind11Test.Animal.Cat, Pybind11Test.Animal.Gender.Male)animal.Attr.Age = 15print("Type:%s, int Type:%s" % (animal.Type, int(animal.Type)))print("Sex:%s, int Sex:%s" % (animal.Sex, int(animal.Sex)))print("Age:%s" % animal.Attr.Age)print(Pybind11Test.Animal.Kind.__members__)print(Pybind11Test.Animal.Gender.__members__)print("运行完毕,请按回车键退出。")input()if __name__ == "__main__":#help(Pybind11Test)main()

相关文章:

使用Pybind11,Python调用C++动态库

最近学习了一下pybind11&#xff0c;使用python来调用C动态库的模式&#xff0c;在某些场景下有用&#xff0c;这里做一个记录。 环境准备 安装python&#xff0c;我这里安装的是3.12版本 下载Pybind11库&#xff0c;这是一个仅包含头文件的轻量级库&#xff0c;使用起来非常…...

提交gitlab

1.gitlab上新建项目 2.git clone url把新项目拉下来 3.git add ./* 把需要提交的文件全部新增 4.git config --global user.email “yetuo.zhuqxsk.local” 身份认证一下 5.git commit -m “asr语音识别-对外服务” 提交 6.git push origin 推送进去 git init git add . git c…...

金慧-综合管理信息系统 LoginBegin.aspx SQL注入复现

0x01 产品描述&#xff1a; 金慧-综合管理信息系统&#xff08;以下简称“金慧综合管理系统”&#xff09;是上海金慧软件有限公司基于多年行业系统研发和实施经验&#xff0c;为各类企业量身定制的一套综合性管理解决方案。该系统旨在通过信息化手段&#xff0c;提升企业的管理…...

RHCSA的学习(4)

一、vi编辑器 &#xff08;1&#xff09;为什么学vi&#xff1f; 所有的Unix Like 系统都会内建 vi 文本编辑器&#xff0c;其他的文本编辑器则不一定会存在&#xff1b; 很多个别软件的编辑接口都会主动呼叫 vi (例如未来会谈到的 crontab, visudo, edquota 等指令)&#x…...

linux-二进制工具

二进制查看工具: 1、hexdump -n length 只格式化输入文件的前length个字节 -C 输出规范的十六进之和ASCII码 -b 单字节八进制显示 -c 单字节字符显示 -d 双字节十进制显示 -o 双字节八进制显示 -x 双字节十六进制显示 -s 从偏移量开始输出 2、od(octal dump) 用于显示文件内…...

《数据结构》学习系列

系列文章目录 一、绪论 二、线性表、链表 三、堆栈、队列 四、数组 五、字符串 六、树...

沂机管理系统/data/Ajax.aspx接口存在SQL注入漏洞

漏洞描述 沂机管理系统/data/Ajax.aspx接口存在SQL注入漏洞&#xff0c;攻击者可以获取服务器权限 漏洞复现 body"后台管理系统演示版" POC GET /data/Ajax.aspx?methodlog_list&page1&limit20&fkey1&fdate12024-10-0100%3A00%3A00&fdate2…...

JVM 内存模型与垃圾回收过程详解

JVM 内存模型与垃圾回收过程详解 文章目录 JVM 内存模型与垃圾回收过程详解1. JVM内存分区1.1 具体分区1.2 JVM内存分区的必要性 2. 垃圾回收2.1 CMS垃圾回收器2.2 G1垃圾回收器2.3 JVM垃圾回收从新生代到老年代 1. JVM内存分区 1.1 具体分区 Java虚拟机&#xff08;JVM&#…...

python:PyPDF2 将多个图片转换为pdf,再合并成一个PDF文件

承上一篇&#xff1a;java&#xff1a;pdfbox 3.0 去除扫描版PDF中文本水印 # 导出扫描版PDF文件中每页的图片文件 java -jar pdfbox-app-3.0.3.jar export:images -prefixtest -i your_book.pdf 导出 Writing image: test-1.jpg Writing image: test-2.jpg Writing image: t…...

Python精选200Tips:186-190

针对序列&#xff08;时间、文本&#xff09;数据的网络结构 续 P186-- 双向LSTM(Bidirectional Long Short-Term Memory 2005)&#xff08;1&#xff09;模型结构说明&#xff08;2&#xff09;创新性说明&#xff08;3&#xff09;示例代码&#xff1a;IMDB电影评论情感分析 …...

C、C++常用数据结构:链表

文章目录 基本概念链表的创建链表结点定义链表创建 链表遍历链表释放链表查找链表删除链表插入测试用例 基本概念 参考&#xff1a;链表基础知识详解&#xff08;非常详细简单易懂&#xff09;-CSDN博客 链表是一种线性存储结构&#xff0c;链表在物理存储上是非连续的&#xf…...

【devops】devops-ansible之剧本变量使用

本站以分享各种运维经验和运维所需要的技能为主 《python零基础入门》:python零基础入门学习 《python运维脚本》: python运维脚本实践 《shell》:shell学习 《terraform》持续更新中:terraform_Aws学习零基础入门到最佳实战 《k8》从问题中去学习k8s 《docker学习》暂未更…...

《Linux从小白到高手》理论篇:一文概览常用Linux重要配置文件

List item 今天继续宅家&#xff0c;闲来无事接着写。本篇是《Linux从小白到高手》理论篇的最后一篇了。本篇集中介绍所有常用的Linux重要配置文件。 用这个命令可以查看配置文件所在的位置&#xff1a;如上图 locate "*.conf" "*.ini" "*.cfg&quo…...

采购管理流程:掌握最后阶段的关键要点

采购管理流程是企业运作中的核心职能之一&#xff0c;涵盖了获取商品和服务的一系列步骤&#xff0c;旨在以高效率和经济效益的方式进行。深入理解该流程的每个环节极为关键&#xff0c;特别是最后阶段&#xff0c;这可确保所有采购活动的圆满完成以及与供应商维持良好关系。 …...

cherry-markdown开源markdown组件详细使用教程

文章目录 前言开发定位目标调研技术方案前提工作量安排数据库表设计实现步骤1、引入依赖2、实现cherry-markdown的vue组件&#xff08;修改上传接口路径&#xff09;3、支持draw.io组件4、支持展示悬浮目录toc前端使用&#xff1a;编辑状态使用cherry-markdown的vue组件前端使用…...

Android SystemUI组件(10)禁用/重启锁屏流程分析

该系列文章总纲链接&#xff1a;专题分纲目录 Android SystemUI组件 本章关键点总结 & 说明&#xff1a; 说明&#xff1a;本章节持续迭代之前章节的思维导图&#xff0c;主要关注左侧上方锁屏分析部分 应用入口处理流程解读 即可。 在 Android 系统中&#xff0c;禁用锁屏…...

【Geeksend邮件营销】外贸邮件中的一些常用语

外贸邮件中的相关术语丰富多样&#xff0c;涉及邮件的开头、正文、结尾以及特定的商务用语。以下是一些常用的外贸邮件术语及其解释&#xff1a; 一、邮件开头用语 1、问候语&#xff1a; Dear [收件人姓名]&#xff0c; Trust this email finds you well. How are you? …...

配置静态ip

背景:因业务需要需要将一台服务器从机房搬到实验室,机房是光纤,实验室是网线,需要重新配置下静态ip 确认网络配置文件(网上没找到,不清楚一下方法对不对) 先随便一个网口连接网线,执行 ifconfig -a 找到带“RUNNING”的(lo不是哈)----eno1 到/etc/sysconfig/network…...

[LeetCode] LCR170. 交易逆序对的总数

题目描述&#xff1a; 在股票交易中&#xff0c;如果前一天的股价高于后一天的股价&#xff0c;则可以认为存在一个「交易逆序对」。请设计一个程序&#xff0c;输入一段时间内的股票交易记录 record&#xff0c;返回其中存在的「交易逆序对」总数。 示例 1: 输入&#xff1a…...

大开眼界,原来指针还能这么玩?

文章目录 第一阶段&#xff1a;基础理解目标&#xff1a;内容&#xff1a;题目&#xff1a;答案解析&#xff1a; 第二阶段&#xff1a;指针与数组目标&#xff1a;内容&#xff1a;题目&#xff1a;答案解析&#xff1a; 第三阶段&#xff1a;指针与字符串目标&#xff1a;内容…...

揭秘选择知识产权管理系统的常见误区,避免踩坑

在当今知识经济时代&#xff0c;知识产权管理对于企业的发展至关重要。为了提高管理效率和效果&#xff0c;许多企业纷纷选择采用知识产权管理系统。然而&#xff0c;在选择过程中&#xff0c;存在着一些容易陷入的误区。 误区一&#xff1a;只关注功能&#xff0c;忽视用户体验…...

计算机组成原理之存储器的分类

1、按存储介质分类&#xff1a; 半导体存储器&#xff1a;使用半导体器件作为存储元件&#xff0c;如TTL和MOS存储器。这类存储器体积小、功耗低、存取时间短&#xff0c;但断电后数据会丢失。 磁表面存储器&#xff1a;使用磁性材料涂覆在金属或塑料基体表面作为存储介质&…...

Linux(不同版本系统包含Ubuntu)下安装mongodb详细教程

一、下载MongoDB 在MongoDB官网下载对应的MongoDB版本&#xff0c;可以点击以下链接快速跳转到下载页面&#xff1a; mongodb官网下载地址 注意选择和自己操作系统一致的platform,可以先查看自己的操作系统 查看操作系统详情 命令&#xff1a; uname -a 如图&#xff1a;操…...

如何扫描HTTP代理:步骤与注意事项

HTTP代理是一个复杂的过程&#xff0c;通常用于寻找可用的代理服务器&#xff0c;以便在网络中实现匿名或加速访问。虽然这个过程可以帮助用户找到适合的代理&#xff0c;但也需要注意合法性和道德问题。本文将介绍如何扫描HTTP代理&#xff0c;并提供一些建议和注意事项。 什…...

【分布式微服务云原生】gRPC与Dubbo:分布式服务通信框架的双雄对决

目录 引言gRPC&#xff1a;Google的高性能RPC框架gRPC通信流程图 Dubbo&#xff1a;阿里巴巴的微服务治理框架Dubbo服务治理流程图 表格&#xff1a;gRPC与Dubbo的比较结论呼吁行动Excel表格&#xff1a;gRPC与Dubbo特性总结 摘要 在构建分布式系统时&#xff0c;选择合适的服务…...

Python | Leetcode Python题解之第450题删除二叉搜索树中的节点

题目&#xff1a; 题解&#xff1a; class Solution:def deleteNode(self, root: Optional[TreeNode], key: int) -> Optional[TreeNode]:cur, curParent root, Nonewhile cur and cur.val ! key:curParent curcur cur.left if cur.val > key else cur.rightif cur i…...

[Linux]从零开始的网站搭建教程

一、谁适合本次教程 学习Linux已经有一阵子了&#xff0c;相信大家对LInux都有一定的认识。本次教程会教大家如何在Linux中搭建一个自己的网站并且实现内网访问。这里我们会演示在Windows中和在Linux中如何搭建自己的网站。当然&#xff0c;如果你没有Linux的基础&#xff0c;这…...

牛客——xay loves or与 __builtin_popcount的使用

xay loves or 题目描述 登录—专业IT笔试面试备考平台_牛客网 运行思路 题目要求我们计算有多少个正整数 yy 满足条件 x \text{ OR } y sx OR ys。这里的“OR”是指按位或运算。为了理解这个问题&#xff0c;我们需要考虑按位或运算的性质。 对于任意两个位 a_iai​ 和 b_…...

Docker学习和部署ry项目

文章目录 停止Docker重启设置开机自启执行docker ps命令&#xff0c;如果不报错&#xff0c;说明安装启动成功2.然后查看数据卷结果3.查看数据卷详情结果4.查看/var/lib/docker/volumes/html/_data目录可以看到与nginx的html目录内容一样&#xff0c;结果如下&#xff1a;5.进入…...

Linux中设置cd命令后直接显示当前目录下的所有文件

Linux中cd命令后默认是不显示该目录下的文件的&#xff0c;略微不方便。换个环境经常遇到需要重新设置的情况&#xff0c;网上已有很多发帖了&#xff0c;这里主要汇总下比较简单且常见的bash与csh下的设置方法。 实现的本质就是将"cd" 与 "ls"组合起来&am…...