最近学习了一下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", ÷, "i"_a, "j"_a); //简写的带命名参数的函数
m.def("divide2", ÷, "i"_a = 20, "j"_a = 2); //提供默认值的函数
m.def("divide3", ÷, py::arg("i") = 30, py::arg("j") = 2); //提供默认值的函数
}
python代码:
import Pybind11Test
def 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:
NAME
Pybind11Test - pybind11 Pybind11Test plugin
FUNCTIONS
add(...) method of builtins.PyCapsule instance
add(arg0: int, arg1: int) -> int
A function that adds two numbers
divide(...) method of builtins.PyCapsule instance
divide(i: int, j: int) -> int
divide2(...) method of builtins.PyCapsule instance
divide2(i: int = 20, j: int = 2) -> int
divide3(...) method of builtins.PyCapsule instance
divide3(i: int = 30, j: int = 2) -> int
multiply(...) method of builtins.PyCapsule instance
multiply(i: int, j: int) -> int
DATA
the_answer = 42
what = 'World'
FILE
c:\workspace\c++\pybind11test\x64\debug\pybind11test.pyd
the_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和setter
py::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 Pybind11Test
def 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 = 25
print(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 Pybind11Test
def 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))
return
if __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类型的实例 animal
animal.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 Pybind11Test
def main():
animal = Pybind11Test.Animal("Garfield", Pybind11Test.Animal.Cat, Pybind11Test.Animal.Gender.Male)
animal.Attr.Age = 15
print("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", ÷, "i"_a, "j"_a); //简写的带命名参数的函数
m.def("divide2", ÷, "i"_a = 20, "j"_a = 2); //提供默认值的函数
m.def("divide3", ÷, 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和setter
py::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类型的实例 animal
animal.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 Pybind11Test
def 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 = 25
print(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 = 15
print("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()